Last active
January 23, 2023 06:23
-
-
Save nythrox/bd958514cb0ff8bf36b5f619a10703bf to your computer and use it in GitHub Desktop.
Flutter/Dart do notation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
main() { | |
// Either<dynamic, int> result | |
final result = Either.Do(() { | |
final num1 = perform(Right(5)); | |
final num2 = perform(Right(2)); | |
return num1 * num2; | |
}); | |
print(result.value); // 10 | |
// Option<int> result2 | |
final result2 = Option.Do(() { | |
final num1 = perform(Some(5)); | |
io(() => print("hi")); | |
final num2 = perform(Some(2)); | |
return num1 * num2; | |
}); // hi | |
print(result2.value); // 10 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Context context; | |
Monad<R> Do<R>(Monad<T> Function<T>(T value) of, R Function() action) { | |
final trace = []; | |
final ctx = Context(trace); | |
step() { | |
final savedContext = context; | |
ctx.pos = 0; | |
try { | |
context = ctx; | |
return of(action()); | |
} on DoNotationBind catch (bind) { | |
final pos = context.pos; | |
return bind.monad.chain((value) { | |
trace.insert(pos, value); | |
ctx.pos++; | |
return step(); | |
}); | |
} on Io catch (exn) { | |
trace.insert(context.pos, exn.action()); | |
ctx.pos++; | |
return step() as Future<T>; | |
} finally { | |
context = savedContext; | |
} | |
} | |
return step(); | |
} | |
final do_ = Do; | |
T perform<T>(Monad<T> monad) { | |
if (context.pos < context.trace.length) return context.trace[context.pos++]; | |
throw DoNotationBind(monad); | |
} | |
class Context { | |
List<dynamic> trace; | |
int pos; | |
Context(this.trace); | |
} | |
abstract class Monad<T> { | |
Monad<T2> map<T2>(T2 Function(T value) mapper); | |
Monad<T2> chain<T2>(Monad<T2> Function(T value) chainer); | |
} | |
class DoNotationBind<T> { | |
final Monad<T> monad; | |
DoNotationBind(this.monad); | |
} | |
T io<T>(T Function() action) { | |
if (context.pos < context.trace.length) return context.trace[context.pos++]; | |
throw Io(action); | |
} | |
class Io<T> { | |
final T Function() action; | |
Io(this.action); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Either<L, R> implements Monad<R> { | |
final R value; | |
final L error; | |
final bool isRight; | |
final bool isLeft; | |
const Either._(this.value, this.error, this.isRight, this.isLeft); | |
static Either<L, R> _left<L, R>(L error) { | |
return Either<L, R>._(null, error, false, true); | |
} | |
static Either<L, R> _right<L, R>(R value) { | |
return Either<L, R>._(value, null, true, false); | |
} | |
static const Left = Either._left; | |
static const Right = Either._right; | |
@override | |
Either<L, R2> chain<R2>(Function(R value) chainer) { | |
return isRight ? chainer(value) : this; | |
} | |
@override | |
Either<L, R2> map<R2>(R2 Function(R value) mapper) { | |
return isRight ? Either.Right(mapper(value)) : this; | |
} | |
static Either<L, R> Do<L, R>(R Function() action) { | |
return do_(<T>(value) => Right(value), action); | |
} | |
} | |
const Right = Either.Right; | |
const Left = Either.Left; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Option<T> implements Monad<T> { | |
final T value; | |
final bool isSome; | |
const Option._(this.value, this.isSome); | |
static Option<T> _some<T>(T value) { | |
return Option<T>._(value, true); | |
} | |
static Option<T> _none<T>() { | |
return Option<T>._(null, false); | |
} | |
static const Some = Option._some; | |
static const None = Option._none; | |
@override | |
Option<T2> chain<T2>(Function(T value) chainer) { | |
return this.isSome ? chainer(value) : this; | |
} | |
@override | |
Option<T2> map<T2>(T2 Function(T value) mapper) { | |
return this.isSome ? Some(mapper(value)) : this; | |
} | |
static Option<T> Do<T>(T Function() action) { | |
return do_(Some, action); | |
} | |
} | |
const Some = Option.Some; | |
const None = Option.None; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment