Either<L, R> class abstract

Author: Petrus Nguyễn Thái Học.

Either is a type that represents either Right (usually represent a "desired" value) or Left (usually represent a "undesired" value or error value).

Elm Result. Haskell Data.Either. Rust Result.

In day-to-day programming, it is fairly common to find ourselves writing functions that can fail. For instance, querying a service may result in a connection issue, or some unexpected JSON response.

To communicate these errors, it has become common practice to throw exceptions; however, exceptions are not tracked in any way, shape, or form by the compiler. To see what kind of exceptions (if any) a function may throw, we have to dig through the source code. Then, to handle these exceptions, we have to make sure we catch them at the call site. This all becomes even more unwieldy when we try to compose exception-throwing procedures.

double throwsSomeStuff(int i) => throw UnimplementedError();

String throwsOtherThings(double d) => throw UnimplementedError();

List<int> moreThrowing(String s) => throw UnimplementedError();

List<int> magic(int i) => moreThrowing( throwsOtherThings( throwsSomeStuff(i) ) );

Assume we happily throw exceptions in our code. Looking at the types of the functions above, any could throw a number of exceptions -- we do not know. When we compose, exceptions from any of the constituent functions can be thrown. Moreover, they may throw the same kind of exception (e.g., ArgumentError) and, thus, it gets tricky tracking exactly where an exception came from.

How then do we communicate an error? By making it explicit in the data type we return.

Either

Either is used to short-circuit a computation upon the first error. By convention, the right side of an Either is used to hold successful values.

Because Either is right-biased, it is possible to define a Monad instance for it. Since we only ever want the computation to continue in the case of Right (as captured by the right-bias nature), we fix the left type parameter and leave the right one free. So, the map and flatMap methods are right-biased.

Implementers
Available Extensions
Annotations
  • @immutable
  • @sealed

Constructors

Either.binding(@monadComprehensions R block(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.
factory
Either.catchError(ErrorMapper<L> errorMapper, R block())
Evaluates the specified block and wrap the result in a Right.
factory
Either.left(L left)
Create a Left.
const
factory
Either.right(R right)
Create a Right.
const
factory

Properties

hashCode int
The hash code for this object.
no setterinherited
isLeft bool
Returns true if this is a Left, false otherwise. Used only for performance instead of fold.
no setter
isRight bool
Returns true if this is a Right, false otherwise. Used only for performance instead of fold.
no setter
runtimeType Type
A representation of the runtime type of the object.
no setterinherited

Methods

all(bool predicate(R value)) bool
Returns true if Left or returns the result of the application of the given predicate to the Right value.
bimap<C, D>({required C leftOperation(L value), required D rightOperation(R value)}) Either<C, D>
Map over Left and Right of this Either
exists(bool predicate(R value)) bool
Returns false if Left or returns the result of the application of the given predicate to the Right value.
findOrNull(bool predicate(R value)) → R?
Returns the Right.value matching the given predicate, or null if this is a Left or Right.value does not match.
flatMap<C>(Either<L, C> f(R value)) Either<L, C>
Binds the given function across Right.
fold<C>({required C ifLeft(L value), required C ifRight(R value)}) → C
Applies ifLeft if this is a Left or ifRight if this is a Right.
foldLeft<C>(C initial, C rightOperation(C acc, R element)) → C
If this is a Right, applies ifRight with initial and Right.value. Returns initial otherwise.
getOrElse(R defaultValue()) → R
Returns the value from this Right or the given argument if this is a Left.
getOrHandle(R defaultValue(L value)) → R
Returns the value from this Right or allows clients to transform the value of Left to the final result.
handleError(R f(L value)) Either<L, R>
Handle any error, potentially recovering from it, by mapping it to an Either value.
handleErrorWith<C>(Either<C, R> f(L value)) Either<C, R>
Handle any error, potentially recovering from it, by mapping it to an Either value.
map<C>(C f(R value)) Either<L, C>
The given function is applied if this is a Right.
mapLeft<C>(C f(L value)) Either<C, R>
The given function is applied if this is a Left.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
orNull() → R?
Returns the Right's value if it exists, otherwise null.
redeem<C>({required C leftOperation(L value), required C rightOperation(R value)}) Either<L, C>
Redeem an Either to an Either by resolving the error or mapping the value R to C.
redeemWith<C, D>({required Either<C, D> leftOperation(L value), required Either<C, D> rightOperation(R value)}) Either<C, D>
Redeem an Either to an Either by resolving the error or mapping the value R to C with an Either.
swap() Either<R, L>
If this is a Left, then return the left value in Right or vice versa.
tap(void f(R value)) Either<L, R>
The given function is applied as a fire and forget effect if this is a Right. When applied the result is ignored and the original Either value is returned.
tapLeft(void f(L value)) Either<L, R>
The given function is applied as a fire and forget effect if this is a Left. When applied the result is ignored and the original Either value is returned.
toString() String
A string representation of this object.
inherited
when<C>({required C ifLeft(Left<L, R> left), required C ifRight(Right<L, R> right)}) → C
Applies ifLeft if this is a Left or ifRight if this is a Right.

Operators

operator ==(Object other) bool
The equality operator.
inherited

Static Methods

catchFutureError<L, R>(ErrorMapper<L> errorMapper, FutureOr<R> block()) Future<Either<L, R>>
Evaluates the specified block and wrap the result in a Right.
catchStreamError<L, R>(ErrorMapper<L> errorMapper, Stream<R> stream) Stream<Either<L, R>>
Transforms data events to Rights and error events to Lefts.
fromNullable<R extends Object>(R? value) Either<void, R>
Returns a Right if value is not null, otherwise a Left containing null.
futureBinding<L, R>(FutureOr<R> block(EitherEffect<L> effect)) Future<Either<L, R>>
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.
parSequenceN<L, R>(Iterable<Future<Either<L, R>> Function()> functions, int? n) Future<Either<L, BuiltList<R>>>
TODO(parSequenceN)
parTraverseN<L, R, T>(Iterable<T> values, Future<Either<L, R>> Function() mapper(T value), int? n) Future<Either<L, BuiltList<R>>>
TODO(parTraverseN)
sequence<L, R>(Iterable<Either<L, R>> values) Either<L, BuiltList<R>>
Sequences all Either values. If one of them is a Left, then it will short-circuit the operation, and returning the first encountered Left.
traverse<L, R, T>(Iterable<T> values, Either<L, R> mapper(T value)) Either<L, BuiltList<R>>
Traverses the values iterable and runs mapper on each element.