Either<L, R>.binding constructor

Either<L, R>.binding(
  1. @monadComprehensions R block(
    1. EitherEffect<L> effect
    )
)

Monad comprehension. Syntactic sugar do-notation. Although using flatMap openly often makes sense, many programmers prefer a syntax that mimics imperative statements (called do-notation in Haskell, perform-notation in OCaml, computation expressions in F#, and for comprehension in Scala). This is only syntactic sugar that disguises a monadic pipeline as a code block.

Calls the specified function block with EitherEffect as its parameter and returns its Either.

When inside a Either.binding block, calling the EitherEffect.bind function will attempt to unwrap the Either and locally return its Right.value. If the Either is a Left, the binding block will terminate with that bind and return that failed-to-bind Left.

You can also use BindEitherExtension.bind instead of EitherEffect.bind for more convenience.

Example

class ExampleError {}

Either<ExampleError, int> provideX() { ... }
Either<ExampleError, int> provideY() { ... }
Either<ExampleError, int> provideZ(int x, int y) { ... }

Either<ExampleError, int> result = Either<ExampleError, int>.binding((e) {
  int x = provideX().bind(e);       // or use `e.bind(provideX())`.
  int y = e.bind(provideY());       // or use `provideY().bind(e)`.
  int z = provideZ(x, y).bind(e);   // or use `e.bind(provideZ(x, y))`.
  return z;
});

NOTE

/// This function can throw an error.
int canThrowAnError() { ... }

// DON'T
Either<ExampleError, int> result = Either<ExampleError, int>.binding((e) {
  int value = canThrowAnError();
});

// DO
ExampleError toExampleError(Object e, StackTrace st) { ... }

Either<ExampleError, int> result = Either<ExampleError, int>.binding((e) {
  int value = Either<ExampleError, int>.catchError(
    toExampleError,
    canThrowAnError
  ).bind(e);
});

Implementation

factory Either.binding(
    @monadComprehensions R Function(EitherEffect<L> effect) block) {
  final eitherEffect = _EitherEffectImpl<L>(_Token());

  try {
    return Either.right(block(eitherEffect));
  } on ControlError<L> catch (e) {
    if (identical(eitherEffect._token, e._token)) {
      return Either.left(e._value);
    } else {
      rethrow;
    }
  }
}