dart_either 1.0.0-beta03 dart_either: ^1.0.0-beta03 copied to clipboard
Small lib for catching errors and turn it into a value.
dart_either #
Author: Petrus Nguyễn Thái Học #
Either monad for Dart language and Flutter framework. Supports Monad comprehension
(both sync
and async
versions).
Credits: port and adapt from Λrrow-kt. #
Liked some of my work? Buy me a coffee (or more likely a beer)
Difference from other implementations (dartz and fpdart) #
I have seen a lot of people importing whole libraries such as dartz and fpdart, ...
but they only use Either
class :). So I decided to write, port and adapt Either
class from Λrrow-kt.
- Inspired by Λrrow-kt, Scala Cats.
- Fully documented, tested and many examples. Every method/function in this library is documented with examples.
- This library is most complete
Either
implementation, which supportsMonad comprehension
(bothsync
andasync
versions). - Very lightweight and simple library (compare to dartz).
Either monad #
Either
is a type that represents either Right
(usually represent a "desired" value)
or Left
(usually represent a "undesired" value or error value).
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
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.
Example:
/// Create an instance of [Right]
final right = Either<String, int>.right(10); // Either.Right(10)
/// Create an instance of [Left]
final left = Either<String, int>.left('none'); // Either.Left(none)
/// Map the right value to a [String]
final mapRight = right.map((a) => 'String: $a'); // Either.Right(String: 10)
/// Map the left value to a [int]
final mapLeft = right.mapLeft((a) => a.length); // Either.Right(10)
/// Return [Left] if the function throws an error.
/// Otherwise return [Right].
final catchError = Either.catchError(
(e, s) => 'Error: $e',
() => int.parse('invalid'),
);
// Either.Left(Error: FormatException: Invalid radix-10 number (at character 1)
// invalid
// ^
// )
/// Extract the value from [Either]
final value1 = right.getOrElse(() => -1); // 10
final value2 = right.getOrHandle((l) => -1); // 10
/// Chain computations
final flatMap = right.flatMap((a) => Either.right(a + 10)); // Either.Right(20)
/// Pattern matching
right.fold(
ifLeft: (l) => print('Left: $l'),
ifRight: (r) => print('Right: $r'),
); // Right: 10
/// Convert to nullable value
final nullableValue = right.orNull(); // 10
Getting started #
In your Dart/Flutter project, add the dependency to your pubspec.yaml
dependencies:
dart_either: ^1.0.0
Use - Documentation #
1. Creation #
1.1. Factory constructors
// Left('Left value')
final left = Either<Object, String>.left('Left value'); // or Left<Object, String>('Left value');
// Right(1)
final right = Either<Object, int>.right(1); // or Right<Object, int>(1);
// Left('Left value')
Either<Object, String>.binding((e) {
final String s = left.bind(e);
final int i = right.bind(e);
return '$s $i';
});
// Left(FormatException(...))
Either.catchError(
(e, s) => 'Error: $e',
() => int.parse('invalid'),
);
1.2. Static methods
- Either.catchFutureError
- Either.catchStreamError
- Either.fromNullable
- Either.futureBinding
- Either.parSequenceN
- Either.parTraverseN
- Either.sequence
- Either.traverse
1.3. Extension methods
2. Operations #
Reference #
Features and bugs #
Please file feature requests and bugs at the issue tracker.