3 ms·
> For Option types to work well and not be an awkward footgun (like C++ std::optional), you also need sum types and pattern matching. Which you can derive from
by nybble41 7y ago
> For Option types to work well and not be an awkward footgun (like C++ std::optional), you also need sum types and pattern matching.
Which you can derive from lexical closures (which Dart has) using Church encoding:
typedef Optional<T> = R Function<R>(R, R Function(T));
Optional<T> None<T>() {
return <R>(R none, R some(T x)) => none;
}
Optional<T> Some<T>(T x) {
return <R>(R none, R some(T x)) => some(x);
}
Optional<B> MapOptional<A,B>(B f(A), Optional<A> option) {
return <R>(R none, R some(B x)) => option(none, (a) => some(f(a)));
}
String OptionalString<T>(Optional<T> option) {
return option('None()', (x) => 'Some(${x.toString()})');
}
void main() {
Optional<num> opt1 = None();
Optional<num> opt2 = Some(4);
print('opt1: ${OptionalString(opt1)}');
print('opt2: ${OptionalString(opt2)}');
Optional<num> opt3 = MapOptional((x) => 2*x, opt2);
print('2*opt2: ${OptionalString(opt3)}');
}
Optional<T> is a sum type, and you pattern match on it by calling the function and providing expressions for the None and Some cases as parameters.
There may be some implementation limits... I managed to crash the compiler at https://dartpad.dartlang.org/ https://dartpad.dartlang.org/ with the first version which had the expression for opt3 inlined into the call to OptionalString(). Declaring it as a separate variable worked around the issue. It's also apparently disallowed to nest generic function types, so you can't have Optional<Optional<T>>; this might be circumvented by wrapping the function type in a class.