diff --git a/example/src/task_either/overview.dart b/example/src/task_either/overview.dart index 22f6f491..0a20cec8 100644 --- a/example/src/task_either/overview.dart +++ b/example/src/task_either/overview.dart @@ -1,5 +1,22 @@ import 'package:fpdart/fpdart.dart'; +/// From [Future] to [TaskEither] +Future imperative(String str) async { + try { + return int.parse(str); + } catch (e) { + return -1; // What does -1 means? 🤨 + } +} + +TaskEither functional(String str) { + return TaskEither.tryCatch( + () async => int.parse(str), + // Clear error 🪄 + (error, stackTrace) => "Parsing error: $error", + ); +} + /// What error is that? What is [dynamic]? Future asyncI() { return Future.error('Some error!') @@ -34,4 +51,6 @@ TaskEither bimapExample(TaskEither taskEither) => TaskEither toTaskEitherExample(Either taskEither) => taskEither.toTaskEither(); -void main() {} +/// Chain [Either] to [TaskEither] +TaskEither binding = + TaskEither.of("String").bindEither(Either.of(20)); diff --git a/lib/src/either.dart b/lib/src/either.dart index 61210fc5..74efff3b 100644 --- a/lib/src/either.dart +++ b/lib/src/either.dart @@ -110,16 +110,6 @@ abstract class Either extends HKT2<_EitherHKT, L, R> Either ap(covariant Either a) => a.flatMap((f) => map(f)); - /// 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 `bind`. - @override - Either flatMap(covariant Either Function(R a) f); - /// If this [Either] is a [Right], then return the result of calling `then`. /// Otherwise return [Left]. @override @@ -159,20 +149,28 @@ abstract class Either extends HKT2<_EitherHKT, L, R> @override Either call(covariant Either chain) => flatMap((_) => chain); - /// If `f` applied on this [Either] as [Right] returns `true`, then return this [Either]. - /// If it returns `false`, return the result of `onFalse` in a [Left]. - Either filterOrElse(bool Function(R r) f, L Function(R r) onFalse) => - flatMap((r) => f(r) ? Either.of(r) : Either.left(onFalse(r))); - + /// {@template fpdart_flat_map_either} /// 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]. + /// {@endtemplate} + /// + /// Same as `bind`. + @override + Either flatMap(covariant Either Function(R a) f); + + /// {@macro fpdart_flat_map_either} /// /// Same as `flatMap`. Either bind(Either Function(R r) f) => flatMap(f); + /// If `f` applied on this [Either] as [Right] returns `true`, then return this [Either]. + /// If it returns `false`, return the result of `onFalse` in a [Left]. + Either filterOrElse(bool Function(R r) f, L Function(R r) onFalse) => + flatMap((r) => f(r) ? Either.of(r) : Either.left(onFalse(r))); + /// Chain a request that returns another [Either], execute it, ignore /// the result, and return the same value as the current [Either]. @override diff --git a/lib/src/task_either.dart b/lib/src/task_either.dart index 43f32fb9..d8d460b8 100644 --- a/lib/src/task_either.dart +++ b/lib/src/task_either.dart @@ -40,6 +40,10 @@ class TaskEither extends HKT2<_TaskEitherHKT, L, R> ), )); + /// Chain an [Either] to [TaskEither] by converting it from sync to async. + TaskEither bindEither(Either either) => + flatMap((_) => either.toTaskEither()); + /// Returns a [TaskEither] that returns a `Right(a)`. @override TaskEither pure(C a) => TaskEither(() async => Right(a)); @@ -200,10 +204,31 @@ class TaskEither extends HKT2<_TaskEitherHKT, L, R> factory TaskEither.fromEither(Either either) => TaskEither(() async => either); - /// Converts a [Future] that may throw to a [Future] that never throws - /// but returns a [Either] instead. + /// {@template fpdart_try_catch_task_either} + /// Execute an async function ([Future]) and convert the result to [Either]: + /// - If the execution is successful, returns a [Right] + /// - If the execution fails (`throw`), then return a [Left] based on `onError` + /// + /// Used to work with [Future] and exceptions using [Either] instead of `try`/`catch`. + /// {@endtemplate} + /// ```dart + /// /// From [Future] to [TaskEither] + /// Future imperative(String str) async { + /// try { + /// return int.parse(str); + /// } catch (e) { + /// return -1; /// What does -1 means? 🤨 + /// } + /// } /// - /// Used to handle asynchronous computations that may throw using [Either]. + /// TaskEither functional(String str) { + /// return TaskEither.tryCatch( + /// () async => int.parse(str), + /// /// Clear error 🪄 + /// (error, stackTrace) => "Parsing error: $error", + /// ); + /// } + /// ``` factory TaskEither.tryCatch(Future Function() run, L Function(Object error, StackTrace stackTrace) onError) => TaskEither(() async { @@ -304,10 +329,7 @@ class TaskEither extends HKT2<_TaskEitherHKT, L, R> ) => traverseListSeq(list, identity); - /// Converts a [Future] that may throw to a [Future] that never throws - /// but returns a [Either] instead. - /// - /// Used to handle asynchronous computations that may throw using [Either]. + /// {@macro fpdart_try_catch_task_either} /// /// It wraps the `TaskEither.tryCatch` factory to make chaining with `flatMap` /// easier. diff --git a/test/src/task_either_test.dart b/test/src/task_either_test.dart index 61b3630a..f4a145c4 100644 --- a/test/src/task_either_test.dart +++ b/test/src/task_either_test.dart @@ -99,6 +99,22 @@ void main() { }); }); + group('bindEither', () { + test('Right', () async { + final task = TaskEither(() async => Either.of(10)); + final ap = task.bindEither(Either.of(2.5)); + final r = await ap.run(); + r.matchTestRight((r) => expect(r, 2.5)); + }); + + test('Left', () async { + final task = TaskEither(() async => Either.left('abc')); + final ap = task.bindEither(Either.of(2.5)); + final r = await ap.run(); + r.matchTestLeft((l) => expect(l, 'abc')); + }); + }); + group('ap', () { test('Right', () async { final task = TaskEither(() async => Either.of(10));