diff --git a/.packages b/.packages index 8ed954a8..208ae8e7 100644 --- a/.packages +++ b/.packages @@ -3,7 +3,7 @@ # # For more info see: https://dart.dev/go/dot-packages-deprecation # -# Generated by pub on 2021-06-15 19:06:22.469619. +# Generated by pub on 2021-06-20 14:14:38.902319. _fe_analyzer_shared:file:///C:/Users/Sandro%20Maglione/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/_fe_analyzer_shared-22.0.0/lib/ analyzer:file:///C:/Users/Sandro%20Maglione/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/analyzer-1.7.0/lib/ args:file:///C:/Users/Sandro%20Maglione/AppData/Roaming/Pub/Cache/hosted/pub.dartlang.org/args-2.1.0/lib/ diff --git a/example/src/function/identity.dart b/example/src/function/identity.dart new file mode 100644 index 00000000..dee6d9c4 --- /dev/null +++ b/example/src/function/identity.dart @@ -0,0 +1,13 @@ +import 'package:fpdart/fpdart.dart'; + +void main() { + final either = Either.of(10); + + /// Without using `identity`, you must write a function to return + /// the input parameter `(l) => l`. + final noId = either.match((l) => l, (r) => '$r'); + + /// Using `identity`/`id`, the function just returns its input parameter. + final withIdentity = either.match(identity, (r) => '$r'); + final withId = either.match(id, (r) => '$r'); +} diff --git a/lib/src/either.dart b/lib/src/either.dart index 45d27aed..714c4994 100644 --- a/lib/src/either.dart +++ b/lib/src/either.dart @@ -1,8 +1,19 @@ import 'function.dart'; import 'option.dart'; +import 'task_either.dart'; import 'tuple.dart'; import 'typeclass/typeclass.export.dart'; +/// Return a `Right(r)`. +/// +/// Shortcut for `Either.of(r)`. +Either right(R r) => Right(r); + +/// Return a `Left(l)`. +/// +/// Shortcut for `Either.left(l)`. +Either left(L l) => Left(l); + /// Tag the [HKT2] interface for the actual [Either]. abstract class _EitherHKT {} @@ -92,6 +103,8 @@ abstract class Either extends HKT2<_EitherHKT, L, R> /// You can extract the value of every [Right] in the chain without /// handling all possible missing cases. /// If any of the functions in the chain returns [Left], the result is [Left]. + /// + /// Same as `bind`. @override Either flatMap(covariant Either Function(R a) f); @@ -135,6 +148,22 @@ abstract class Either extends HKT2<_EitherHKT, L, R> Either filterOrElse(bool Function(R r) f, L Function(R r) onFalse) => flatMap((r) => f(r) ? Either.of(r) : Either.left(onFalse(r))); + /// Used to chain multiple functions that return a [Either]. + /// + /// You can extract the value of every [Right] in the chain without + /// handling all possible missing cases. + /// If any of the functions in the chain returns [Left], the result is [Left]. + /// + /// Same as `flatMap`. + Either bind(Either Function(R r) f) => flatMap(f); + + /// Used to chain multiple functions that return a `Future`. + /// + /// When this value is [Right], it returns a [TaskEither] that will resolve to + /// the result of calling `f`. + /// Otherwise, if this value is [Left], it returns `TaskEither.left()`. + TaskEither bindFuture(Future> Function(R r) f); + /// If the [Either] is [Left], then change its value from type `L` to /// type `C` using function `f`. Either mapLeft(C Function(L a) f); @@ -176,8 +205,16 @@ abstract class Either extends HKT2<_EitherHKT, L, R> R getOrElse(R Function(L l) orElse); /// Execute `onLeft` when value is [Left], otherwise execute `onRight`. + /// + /// Same as `fold`. C match(C Function(L l) onLeft, C Function(R r) onRight); + /// Execute `onLeft` when value is [Left], otherwise execute `onRight`. + /// + /// Same as `match`. + C fold(C Function(L l) onLeft, C Function(R r) onRight) => + match(onLeft, onRight); + /// Return `true` when value of `r` is equal to the value inside this [Either]. /// If this [Either] is [Left], then return `false`. bool elem(R r, Eq eq); @@ -320,6 +357,10 @@ class Right extends Either { @override String toString() => 'Right($_value)'; + + @override + TaskEither bindFuture(Future> Function(R r) f) => + TaskEither(() async => f(_value)); } class Left extends Either { @@ -396,4 +437,8 @@ class Left extends Either { @override String toString() => 'Left($_value)'; + + @override + TaskEither bindFuture(Future> Function(R r) f) => + TaskEither.left(_value); } diff --git a/lib/src/function.dart b/lib/src/function.dart index a79ec162..b14e5d0f 100644 --- a/lib/src/function.dart +++ b/lib/src/function.dart @@ -1,8 +1,41 @@ typedef Endo = A Function(A a); /// Returns the given `a`. +/// +/// Same as `id`. +/// +/// Shortcut function to return the input parameter: +/// ```dart +/// final either = Either.of(10); +/// +/// /// Without using `identity`, you must write a function to return +/// /// the input parameter `(l) => l`. +/// final noId = either.match((l) => l, (r) => '$r'); +/// +/// /// Using `identity`/`id`, the function just returns its input parameter. +/// final withIdentity = either.match(identity, (r) => '$r'); +/// final withId = either.match(id, (r) => '$r'); +/// ``` T identity(T a) => a; +/// Returns the given `a`. +/// +/// Same as `identity`. +/// +/// Shortcut function to return the input parameter: +/// ```dart +/// final either = Either.of(10); +/// +/// /// Without using `identity`, you must write a function to return +/// /// the input parameter `(l) => l`. +/// final noId = either.match((l) => l, (r) => '$r'); +/// +/// /// Using `identity`/`id`, the function just returns its input parameter. +/// final withIdentity = either.match(identity, (r) => '$r'); +/// final withId = either.match(id, (r) => '$r'); +/// ``` +T id(T a) => a; + /// Converts a binary function into a unary function that returns a unary function. /// /// Enables the use of partial application of functions. diff --git a/pubspec.yaml b/pubspec.yaml index d433d0e4..953d6506 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: fpdart -version: 0.0.4 +version: 0.0.5 homepage: https://www.sandromaglione.com/ repository: https://github.com/SandroMaglione/fpdart description: Functional programming in Dart and Flutter. All the main functional programming types and patterns fully documented, tested, and with examples. diff --git a/test/src/either_test.dart b/test/src/either_test.dart index bb046161..c27f5e4b 100644 --- a/test/src/either_test.dart +++ b/test/src/either_test.dart @@ -721,4 +721,36 @@ void main() { }); }); }); + + group('bind', () { + test('Right', () { + final either1 = Either.of(10); + final result = either1.bind((r) => Either.of(r + 10)); + expect(result.getOrElse((l) => 0), 20); + }); + + test('Left', () { + final either1 = Either.left('String'); + final result = either1.bind((r) => Either.of(r + 10)); + expect(result.getOrElse((l) => 0), 0); + expect(result.getLeft().getOrElse(() => ''), 'String'); + }); + }); + + group('bindFuture', () { + test('Right', () async { + final either1 = Either.of(10); + final asyncEither = either1.bindFuture((r) async => Either.of(r + 10)); + final result = await asyncEither.run(); + expect(result.getOrElse((l) => 0), 20); + }); + + test('Left', () async { + final either1 = Either.left('String'); + final asyncEither = either1.bindFuture((r) async => Either.of(r + 10)); + final result = await asyncEither.run(); + expect(result.getOrElse((l) => 0), 0); + expect(result.getLeft().getOrElse(() => ''), 'String'); + }); + }); }