From f9ece2cd8940c572cd23403368e1a9c34f5ecd41 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 16 Feb 2024 13:04:27 +0100 Subject: [PATCH 01/91] experiment with shared `Effect` class --- packages/fpdart/example/effect/main.dart | 31 ++++++ packages/fpdart/lib/fpdart.dart | 1 + packages/fpdart/lib/src/effect.dart | 129 +++++++++++++++++++++++ packages/fpdart/pubspec.yaml | 2 +- 4 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 packages/fpdart/example/effect/main.dart create mode 100644 packages/fpdart/lib/src/effect.dart diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart new file mode 100644 index 0000000..7df9d40 --- /dev/null +++ b/packages/fpdart/example/effect/main.dart @@ -0,0 +1,31 @@ +import 'package:fpdart/fpdart.dart'; + +void main() async { + final asyncEither = AsyncEither.tryFuture( + () async => 10, + (_, __) => "", + ); + + final syncEither = SyncEither.trySync( + () => 10, + (_, __) => "", + ); + + final syncC = Sync.make(() => 10).flatMap( + (r) => Sync.value(r + 20), + ); + + final doing = doEffect<void, String, int>( + (_) async { + final asyncEV = await _(asyncEither); + final syncEV = await _(syncEither); + final syncV = await _(syncC); + return asyncEV + syncEV + syncV; + }, + ); + + print(doing); + + final run = await doing.runEffect(null); + print(run); +} diff --git a/packages/fpdart/lib/fpdart.dart b/packages/fpdart/lib/fpdart.dart index a3d852b..e366ab3 100644 --- a/packages/fpdart/lib/fpdart.dart +++ b/packages/fpdart/lib/fpdart.dart @@ -1,4 +1,5 @@ export 'src/date.dart'; +export 'src/effect.dart'; export 'src/either.dart'; export 'src/extension/extension.export.dart'; export 'src/function.dart'; diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart new file mode 100644 index 0000000..6c47487 --- /dev/null +++ b/packages/fpdart/lib/src/effect.dart @@ -0,0 +1,129 @@ +import 'dart:async'; + +import 'package:fpdart/fpdart.dart'; + +import 'either.dart'; + +final class _EffectThrow<L> { + final L value; + const _EffectThrow(this.value); +} + +typedef DoAdapterEffect<E, L> = Future<A> Function<A>(Effect<E, L, A>); + +DoAdapterEffect<E, L> _doAdapter<E, L>(E env) => + <A>(effect) => effect.runEffect(env).then( + (either) => either.getOrElse((l) => throw _EffectThrow(l)), + ); + +typedef DoFunctionEffect<E, L, A> = Future<A> Function(DoAdapterEffect<E, L> _); + +typedef UnsafeRun<E, L, R> = FutureOr<Either<L, R>> Function(E env); + +final class Effect<E, L, R> { + final UnsafeRun<E, L, R> _unsafeRun; + + const Effect._(this._unsafeRun); + + factory Effect.tryFuture( + FutureOr<R> Function() execute, + L Function(Object error, StackTrace stackTrace) onError, + ) => + Effect._( + (env) async { + try { + return Either.right(await execute()); + } catch (e, s) { + return Either.left(onError(e, s)); + } + }, + ); + + Future<Either<L, R>> runEffect(E env) async => _unsafeRun(env); + + Effect<E, L, C> flatMap<C>( + Effect<E, L, C> Function(R r) f, + ) => + Effect._( + (env) => runEffect(env).then( + (either) async => either.match( + left, + (r) => f(r).runEffect(env), + ), + ), + ); + + @override + String toString() { + return "Effect(${_unsafeRun.runtimeType})"; + } +} + +final class AsyncEither<L, R> extends Effect<void, L, R> { + const AsyncEither._(UnsafeRun<void, L, R> run) : super._(run); + + factory AsyncEither._fromEffect(Effect<void, L, R> effect) => + AsyncEither._(effect.runEffect); + + factory AsyncEither.tryFuture( + FutureOr<R> Function() execute, + L Function(Object error, StackTrace stackTrace) onError, + ) => + AsyncEither._fromEffect( + Effect<void, L, R>.tryFuture(execute, onError), + ); +} + +final class SyncEither<L, R> extends Effect<void, L, R> { + const SyncEither._(UnsafeRun<void, L, R> run) : super._(run); + + factory SyncEither._fromEffect(Effect<void, L, R> effect) => + SyncEither._(effect.runEffect); + + factory SyncEither.trySync( + R Function() execute, + L Function(Object error, StackTrace stackTrace) onError, + ) => + SyncEither._fromEffect( + Effect<void, L, R>.tryFuture(execute, onError), + ); +} + +final class Sync<R> extends Effect<void, Never, R> { + const Sync._(UnsafeRun<void, Never, R> run) : super._(run); + + factory Sync._fromEffect(Effect<void, Never, R> effect) => + Sync._(effect.runEffect); + + factory Sync.make( + R Function() execute, + ) => + Sync._fromEffect( + Effect<void, Never, R>.tryFuture( + execute, + (_, __) => throw Exception( + "Error when building Sync.make", + ), + ), + ); + + factory Sync.value(R value) => Sync._( + (_) => Either.right(value), + ); + + @override + Sync<C> flatMap<C>(covariant Sync<C> Function(R r) f) { + return Sync._fromEffect(super.flatMap(f)); + } +} + +Effect<E, L, R> doEffect<E, L, R>(DoFunctionEffect<E, L, R> f) => + Effect<E, L, R>._( + (env) async { + try { + return Either.of(await f(_doAdapter<E, L>(env))); + } on _EffectThrow<L> catch (e) { + return Either.left(e.value); + } + }, + ); diff --git a/packages/fpdart/pubspec.yaml b/packages/fpdart/pubspec.yaml index 9f4b550..7a0873b 100644 --- a/packages/fpdart/pubspec.yaml +++ b/packages/fpdart/pubspec.yaml @@ -10,7 +10,7 @@ documentation: https://www.sandromaglione.com/course/fpdart-functional-programmi issue_tracker: https://github.com/SandroMaglione/fpdart/issues environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" dev_dependencies: lints: ^2.0.1 From 194db2779f4edb9527551e7ceb0db9b275ae47cb Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 16 Feb 2024 18:05:06 +0100 Subject: [PATCH 02/91] `IEffect` shared interface --- packages/fpdart/example/effect/main.dart | 2 +- packages/fpdart/lib/src/async_either.dart | 21 +++++ packages/fpdart/lib/src/effect.dart | 103 +++++++--------------- packages/fpdart/lib/src/sync.dart | 41 +++++++++ packages/fpdart/lib/src/sync_either.dart | 21 +++++ packages/fpdart/pubspec.yaml | 3 + 6 files changed, 120 insertions(+), 71 deletions(-) create mode 100644 packages/fpdart/lib/src/async_either.dart create mode 100644 packages/fpdart/lib/src/sync.dart create mode 100644 packages/fpdart/lib/src/sync_either.dart diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index 7df9d40..d42c4e0 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -26,6 +26,6 @@ void main() async { print(doing); - final run = await doing.runEffect(null); + final run = await doing(null); print(run); } diff --git a/packages/fpdart/lib/src/async_either.dart b/packages/fpdart/lib/src/async_either.dart new file mode 100644 index 0000000..90b95fa --- /dev/null +++ b/packages/fpdart/lib/src/async_either.dart @@ -0,0 +1,21 @@ +part of 'effect.dart'; + +final class AsyncEither<L, R> extends Effect<void, L, R> { + const AsyncEither._(UnsafeRun<void, L, R> run) : super._(run); + + factory AsyncEither._fromEffect(Effect<void, L, R> effect) => + AsyncEither._(effect._runEffect); + + factory AsyncEither.tryFuture( + FutureOr<R> Function() execute, + L Function(Object error, StackTrace stackTrace) onError, + ) => + AsyncEither._fromEffect( + Effect<void, L, R>.tryFuture(execute, onError), + ); + + @override + AsyncEither<L, C> flatMap<C>(AsyncEither<L, C> Function(R r) f) { + return AsyncEither._fromEffect(super.flatMap(f)); + } +} diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 6c47487..529dc02 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -1,18 +1,23 @@ import 'dart:async'; import 'package:fpdart/fpdart.dart'; +import 'package:meta/meta.dart'; import 'either.dart'; +part 'async_either.dart'; +part 'sync.dart'; +part 'sync_either.dart'; + final class _EffectThrow<L> { final L value; const _EffectThrow(this.value); } -typedef DoAdapterEffect<E, L> = Future<A> Function<A>(Effect<E, L, A>); +typedef DoAdapterEffect<E, L> = Future<A> Function<A>(IEffect<E, L, A>); DoAdapterEffect<E, L> _doAdapter<E, L>(E env) => - <A>(effect) => effect.runEffect(env).then( + <A>(effect) => effect._runEffect(env).then( (either) => either.getOrElse((l) => throw _EffectThrow(l)), ); @@ -20,35 +25,21 @@ typedef DoFunctionEffect<E, L, A> = Future<A> Function(DoAdapterEffect<E, L> _); typedef UnsafeRun<E, L, R> = FutureOr<Either<L, R>> Function(E env); -final class Effect<E, L, R> { +abstract interface class IEffect<E, L, R> { final UnsafeRun<E, L, R> _unsafeRun; + const IEffect._(this._unsafeRun); - const Effect._(this._unsafeRun); + Future<Either<L, R>> _runEffect(E env) async => _unsafeRun(env); - factory Effect.tryFuture( - FutureOr<R> Function() execute, - L Function(Object error, StackTrace stackTrace) onError, + @mustBeOverridden + IEffect<E, L, C> flatMap<C>( + IEffect<E, L, C> Function(R r) f, ) => Effect._( - (env) async { - try { - return Either.right(await execute()); - } catch (e, s) { - return Either.left(onError(e, s)); - } - }, - ); - - Future<Either<L, R>> runEffect(E env) async => _unsafeRun(env); - - Effect<E, L, C> flatMap<C>( - Effect<E, L, C> Function(R r) f, - ) => - Effect._( - (env) => runEffect(env).then( + (env) => _runEffect(env).then( (either) async => either.match( left, - (r) => f(r).runEffect(env), + (r) => f(r)._runEffect(env), ), ), ); @@ -59,61 +50,33 @@ final class Effect<E, L, R> { } } -final class AsyncEither<L, R> extends Effect<void, L, R> { - const AsyncEither._(UnsafeRun<void, L, R> run) : super._(run); - - factory AsyncEither._fromEffect(Effect<void, L, R> effect) => - AsyncEither._(effect.runEffect); +final class Effect<E, L, R> extends IEffect<E, L, R> { + const Effect._(UnsafeRun<E, L, R> run) : super._(run); - factory AsyncEither.tryFuture( + factory Effect.tryFuture( FutureOr<R> Function() execute, L Function(Object error, StackTrace stackTrace) onError, ) => - AsyncEither._fromEffect( - Effect<void, L, R>.tryFuture(execute, onError), - ); -} - -final class SyncEither<L, R> extends Effect<void, L, R> { - const SyncEither._(UnsafeRun<void, L, R> run) : super._(run); - - factory SyncEither._fromEffect(Effect<void, L, R> effect) => - SyncEither._(effect.runEffect); - - factory SyncEither.trySync( - R Function() execute, - L Function(Object error, StackTrace stackTrace) onError, - ) => - SyncEither._fromEffect( - Effect<void, L, R>.tryFuture(execute, onError), + Effect._( + (env) async { + try { + return Either.right(await execute()); + } catch (e, s) { + return Either.left(onError(e, s)); + } + }, ); -} -final class Sync<R> extends Effect<void, Never, R> { - const Sync._(UnsafeRun<void, Never, R> run) : super._(run); + Future<Either<L, R>> call(E env) => _runEffect(env); - factory Sync._fromEffect(Effect<void, Never, R> effect) => - Sync._(effect.runEffect); - - factory Sync.make( - R Function() execute, - ) => - Sync._fromEffect( - Effect<void, Never, R>.tryFuture( - execute, - (_, __) => throw Exception( - "Error when building Sync.make", - ), - ), - ); - - factory Sync.value(R value) => Sync._( - (_) => Either.right(value), - ); + @override + String toString() { + return "Effect(${_unsafeRun.runtimeType})"; + } @override - Sync<C> flatMap<C>(covariant Sync<C> Function(R r) f) { - return Sync._fromEffect(super.flatMap(f)); + Effect<E, L, C> flatMap<C>(covariant Effect<E, L, C> Function(R r) f) { + return Effect._(super.flatMap(f)._unsafeRun); } } diff --git a/packages/fpdart/lib/src/sync.dart b/packages/fpdart/lib/src/sync.dart new file mode 100644 index 0000000..3ec803a --- /dev/null +++ b/packages/fpdart/lib/src/sync.dart @@ -0,0 +1,41 @@ +part of "effect.dart"; + +final class Sync<R> extends IEffect<void, Never, R> { + const Sync._(UnsafeRun<void, Never, R> run) : super._(run); + + factory Sync._fromEffect(IEffect<void, Never, R> effect) => + Sync._(effect._runEffect); + + factory Sync.make( + R Function() execute, + ) => + Sync._fromEffect( + Effect<void, Never, R>.tryFuture( + execute, + (_, __) => throw Exception( + "Error when building Sync.make", + ), + ), + ); + + factory Sync.value(R value) => Sync._( + (_) => Either.right(value), + ); + + @override + Sync<C> flatMap<C>(covariant Sync<C> Function(R r) f) { + return Sync._fromEffect(super.flatMap(f)); + } + + R call() { + final run = _unsafeRun(null); + if (run is Future) { + throw Exception("Error running an async Sync"); + } + + return run.match( + (_) => throw Exception("Invalid Sync Left result"), + (r) => r, + ); + } +} diff --git a/packages/fpdart/lib/src/sync_either.dart b/packages/fpdart/lib/src/sync_either.dart new file mode 100644 index 0000000..ea3ffa2 --- /dev/null +++ b/packages/fpdart/lib/src/sync_either.dart @@ -0,0 +1,21 @@ +part of 'effect.dart'; + +final class SyncEither<L, R> extends Effect<void, L, R> { + const SyncEither._(UnsafeRun<void, L, R> run) : super._(run); + + factory SyncEither._fromEffect(Effect<void, L, R> effect) => + SyncEither._(effect._runEffect); + + factory SyncEither.trySync( + R Function() execute, + L Function(Object error, StackTrace stackTrace) onError, + ) => + SyncEither._fromEffect( + Effect<void, L, R>.tryFuture(execute, onError), + ); + + @override + SyncEither<L, C> flatMap<C>(SyncEither<L, C> Function(R r) f) { + return SyncEither._fromEffect(super.flatMap(f)); + } +} diff --git a/packages/fpdart/pubspec.yaml b/packages/fpdart/pubspec.yaml index 7a0873b..23e63d7 100644 --- a/packages/fpdart/pubspec.yaml +++ b/packages/fpdart/pubspec.yaml @@ -12,6 +12,9 @@ issue_tracker: https://github.com/SandroMaglione/fpdart/issues environment: sdk: ">=3.3.0 <4.0.0" +dependencies: + meta: ^1.12.0 + dev_dependencies: lints: ^2.0.1 test: ^1.23.1 From d44181c247573bba2c20c500a342a4df6ff77821 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 16 Feb 2024 18:32:38 +0100 Subject: [PATCH 03/91] `Either` as `IEffect` --- packages/fpdart/example/effect/main.dart | 3 ++- packages/fpdart/lib/src/effect.dart | 31 +++++++++++++----------- packages/fpdart/lib/src/exit.dart | 15 ++++++++++++ packages/fpdart/lib/src/n_either.dart | 25 +++++++++++++++++++ packages/fpdart/lib/src/sync.dart | 10 ++++---- 5 files changed, 64 insertions(+), 20 deletions(-) create mode 100644 packages/fpdart/lib/src/exit.dart create mode 100644 packages/fpdart/lib/src/n_either.dart diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index d42c4e0..a380d6b 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -20,7 +20,8 @@ void main() async { final asyncEV = await _(asyncEither); final syncEV = await _(syncEither); final syncV = await _(syncC); - return asyncEV + syncEV + syncV; + final eitherV = await _(NRight(10)); + return asyncEV + syncEV + syncV + eitherV; }, ); diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 529dc02..ad5d4d7 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -1,11 +1,11 @@ import 'dart:async'; -import 'package:fpdart/fpdart.dart'; import 'package:meta/meta.dart'; -import 'either.dart'; +import 'exit.dart'; part 'async_either.dart'; +part 'n_either.dart'; part 'sync.dart'; part 'sync_either.dart'; @@ -18,18 +18,21 @@ typedef DoAdapterEffect<E, L> = Future<A> Function<A>(IEffect<E, L, A>); DoAdapterEffect<E, L> _doAdapter<E, L>(E env) => <A>(effect) => effect._runEffect(env).then( - (either) => either.getOrElse((l) => throw _EffectThrow(l)), + (exit) => switch (exit) { + Failure(value: final value) => throw _EffectThrow(value), + Success(value: final value) => value, + }, ); typedef DoFunctionEffect<E, L, A> = Future<A> Function(DoAdapterEffect<E, L> _); -typedef UnsafeRun<E, L, R> = FutureOr<Either<L, R>> Function(E env); +typedef UnsafeRun<E, L, R> = FutureOr<Exit<L, R>> Function(E env); abstract interface class IEffect<E, L, R> { final UnsafeRun<E, L, R> _unsafeRun; const IEffect._(this._unsafeRun); - Future<Either<L, R>> _runEffect(E env) async => _unsafeRun(env); + Future<Exit<L, R>> _runEffect(E env) async => _unsafeRun(env); @mustBeOverridden IEffect<E, L, C> flatMap<C>( @@ -37,10 +40,10 @@ abstract interface class IEffect<E, L, R> { ) => Effect._( (env) => _runEffect(env).then( - (either) async => either.match( - left, - (r) => f(r)._runEffect(env), - ), + (exit) async => switch (exit) { + Failure(value: final value) => Future.value(Exit.failure(value)), + Success(value: final value) => f(value)._runEffect(env), + }, ), ); @@ -60,14 +63,14 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect._( (env) async { try { - return Either.right(await execute()); + return Exit.success(await execute()); } catch (e, s) { - return Either.left(onError(e, s)); + return Exit.failure(onError(e, s)); } }, ); - Future<Either<L, R>> call(E env) => _runEffect(env); + Future<Exit<L, R>> call(E env) => _runEffect(env); @override String toString() { @@ -84,9 +87,9 @@ Effect<E, L, R> doEffect<E, L, R>(DoFunctionEffect<E, L, R> f) => Effect<E, L, R>._( (env) async { try { - return Either.of(await f(_doAdapter<E, L>(env))); + return Exit.success(await f(_doAdapter<E, L>(env))); } on _EffectThrow<L> catch (e) { - return Either.left(e.value); + return Exit.failure(e.value); } }, ); diff --git a/packages/fpdart/lib/src/exit.dart b/packages/fpdart/lib/src/exit.dart new file mode 100644 index 0000000..d2a3765 --- /dev/null +++ b/packages/fpdart/lib/src/exit.dart @@ -0,0 +1,15 @@ +sealed class Exit<L, R> { + const Exit(); + factory Exit.success(R value) => Success(value); + factory Exit.failure(L value) => Failure(value); +} + +class Success<L, R> extends Exit<L, R> { + final R value; + const Success(this.value); +} + +class Failure<L, R> extends Exit<L, R> { + final L value; + const Failure(this.value); +} diff --git a/packages/fpdart/lib/src/n_either.dart b/packages/fpdart/lib/src/n_either.dart new file mode 100644 index 0000000..62d6bcc --- /dev/null +++ b/packages/fpdart/lib/src/n_either.dart @@ -0,0 +1,25 @@ +part of "effect.dart"; + +sealed class NEither<L, R> extends IEffect<void, L, R> { + const NEither._(UnsafeRun<void, L, R> run) : super._(run); + + @override + NEither<L, C> flatMap<C>(covariant NEither<L, C> Function(R r) f) { + return switch (this) { + NLeft(value: final value) => NLeft(value), + NRight(value: final value) => f(value), + }; + } +} + +// ignore: missing_override_of_must_be_overridden +class NRight<L, R> extends NEither<L, R> { + final R value; + NRight(this.value) : super._((_) => Exit.success(value)); +} + +// ignore: missing_override_of_must_be_overridden +class NLeft<L, R> extends NEither<L, R> { + final L value; + NLeft(this.value) : super._((_) => Exit.failure(value)); +} diff --git a/packages/fpdart/lib/src/sync.dart b/packages/fpdart/lib/src/sync.dart index 3ec803a..e4c5827 100644 --- a/packages/fpdart/lib/src/sync.dart +++ b/packages/fpdart/lib/src/sync.dart @@ -19,7 +19,7 @@ final class Sync<R> extends IEffect<void, Never, R> { ); factory Sync.value(R value) => Sync._( - (_) => Either.right(value), + (_) => Exit.success(value), ); @override @@ -33,9 +33,9 @@ final class Sync<R> extends IEffect<void, Never, R> { throw Exception("Error running an async Sync"); } - return run.match( - (_) => throw Exception("Invalid Sync Left result"), - (r) => r, - ); + return switch (run) { + Failure() => throw Exception("Invalid Sync Left result"), + Success(value: final value) => value, + }; } } From 3861235b0851cba6b5784b7aa27157fd8ec5dcb0 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 16 Feb 2024 18:36:20 +0100 Subject: [PATCH 04/91] `Async` from `IEffect` --- packages/fpdart/example/effect/main.dart | 9 +++++- packages/fpdart/lib/src/async.dart | 41 ++++++++++++++++++++++++ packages/fpdart/lib/src/effect.dart | 1 + 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 packages/fpdart/lib/src/async.dart diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index a380d6b..3d7a18e 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -1,4 +1,5 @@ import 'package:fpdart/fpdart.dart'; +import 'package:fpdart/src/effect.dart'; void main() async { final asyncEither = AsyncEither.tryFuture( @@ -15,13 +16,19 @@ void main() async { (r) => Sync.value(r + 20), ); + final asyncC = Async.make(() => 10).flatMap( + (r) => Async.value(r + 20), + ); + final doing = doEffect<void, String, int>( (_) async { final asyncEV = await _(asyncEither); + // await _(NLeft("10")); final syncEV = await _(syncEither); final syncV = await _(syncC); + final asyncV = await _(asyncC); final eitherV = await _(NRight(10)); - return asyncEV + syncEV + syncV + eitherV; + return asyncEV + syncEV + syncV + eitherV + asyncV; }, ); diff --git a/packages/fpdart/lib/src/async.dart b/packages/fpdart/lib/src/async.dart new file mode 100644 index 0000000..765d354 --- /dev/null +++ b/packages/fpdart/lib/src/async.dart @@ -0,0 +1,41 @@ +part of "effect.dart"; + +final class Async<R> extends IEffect<void, Never, R> { + const Async._(UnsafeRun<void, Never, R> run) : super._(run); + + factory Async._fromEffect(IEffect<void, Never, R> effect) => + Async._(effect._runEffect); + + factory Async.make( + R Function() execute, + ) => + Async._fromEffect( + Effect<void, Never, R>.tryFuture( + execute, + (_, __) => throw Exception( + "Error when building Async.make", + ), + ), + ); + + factory Async.value(R value) => Async._( + (_) => Exit.success(value), + ); + + @override + Async<C> flatMap<C>(covariant Async<C> Function(R r) f) { + return Async._fromEffect(super.flatMap(f)); + } + + Future<R> call() async { + final run = _unsafeRun(null); + if (run is! Future) { + throw Exception("Error running a sync Async"); + } + + return switch (await run) { + Failure() => throw Exception("Invalid Async Left result"), + Success(value: final value) => value, + }; + } +} diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index ad5d4d7..8bff83e 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -4,6 +4,7 @@ import 'package:meta/meta.dart'; import 'exit.dart'; +part 'async.dart'; part 'async_either.dart'; part 'n_either.dart'; part 'sync.dart'; From 0fec429be470bcd4f4111bba30cf280283c19010 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 16 Feb 2024 18:40:50 +0100 Subject: [PATCH 05/91] `call` run --- packages/fpdart/lib/src/async_either.dart | 8 +++++--- packages/fpdart/lib/src/sync_either.dart | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/fpdart/lib/src/async_either.dart b/packages/fpdart/lib/src/async_either.dart index 90b95fa..c870ebc 100644 --- a/packages/fpdart/lib/src/async_either.dart +++ b/packages/fpdart/lib/src/async_either.dart @@ -1,9 +1,9 @@ part of 'effect.dart'; -final class AsyncEither<L, R> extends Effect<void, L, R> { +final class AsyncEither<L, R> extends IEffect<void, L, R> { const AsyncEither._(UnsafeRun<void, L, R> run) : super._(run); - factory AsyncEither._fromEffect(Effect<void, L, R> effect) => + factory AsyncEither._fromEffect(IEffect<void, L, R> effect) => AsyncEither._(effect._runEffect); factory AsyncEither.tryFuture( @@ -15,7 +15,9 @@ final class AsyncEither<L, R> extends Effect<void, L, R> { ); @override - AsyncEither<L, C> flatMap<C>(AsyncEither<L, C> Function(R r) f) { + AsyncEither<L, C> flatMap<C>(covariant AsyncEither<L, C> Function(R r) f) { return AsyncEither._fromEffect(super.flatMap(f)); } + + Future<Exit<L, R>> call() async => _unsafeRun(null); } diff --git a/packages/fpdart/lib/src/sync_either.dart b/packages/fpdart/lib/src/sync_either.dart index ea3ffa2..af84949 100644 --- a/packages/fpdart/lib/src/sync_either.dart +++ b/packages/fpdart/lib/src/sync_either.dart @@ -1,9 +1,9 @@ part of 'effect.dart'; -final class SyncEither<L, R> extends Effect<void, L, R> { +final class SyncEither<L, R> extends IEffect<void, L, R> { const SyncEither._(UnsafeRun<void, L, R> run) : super._(run); - factory SyncEither._fromEffect(Effect<void, L, R> effect) => + factory SyncEither._fromEffect(IEffect<void, L, R> effect) => SyncEither._(effect._runEffect); factory SyncEither.trySync( @@ -15,7 +15,16 @@ final class SyncEither<L, R> extends Effect<void, L, R> { ); @override - SyncEither<L, C> flatMap<C>(SyncEither<L, C> Function(R r) f) { + SyncEither<L, C> flatMap<C>(covariant SyncEither<L, C> Function(R r) f) { return SyncEither._fromEffect(super.flatMap(f)); } + + Exit<L, R> call() { + final run = _unsafeRun(null); + if (run is Future) { + throw Exception("Error running an async Sync"); + } + + return run; + } } From 289085ada02ebb5a199cc9c5bb2992268219a28e Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 10 Mar 2024 15:04:36 +0900 Subject: [PATCH 06/91] no Sync/Async/Either, just `Effect` --- packages/fpdart/example/effect/main.dart | 27 +++------------ packages/fpdart/lib/src/async.dart | 41 ----------------------- packages/fpdart/lib/src/async_either.dart | 23 ------------- packages/fpdart/lib/src/effect.dart | 8 ++--- packages/fpdart/lib/src/sync.dart | 41 ----------------------- packages/fpdart/lib/src/sync_either.dart | 30 ----------------- 6 files changed, 7 insertions(+), 163 deletions(-) delete mode 100644 packages/fpdart/lib/src/async.dart delete mode 100644 packages/fpdart/lib/src/async_either.dart delete mode 100644 packages/fpdart/lib/src/sync.dart delete mode 100644 packages/fpdart/lib/src/sync_either.dart diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index 3d7a18e..f786e83 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -2,33 +2,16 @@ import 'package:fpdart/fpdart.dart'; import 'package:fpdart/src/effect.dart'; void main() async { - final asyncEither = AsyncEither.tryFuture( - () async => 10, - (_, __) => "", - ); - - final syncEither = SyncEither.trySync( - () => 10, - (_, __) => "", - ); - - final syncC = Sync.make(() => 10).flatMap( - (r) => Sync.value(r + 20), - ); - - final asyncC = Async.make(() => 10).flatMap( - (r) => Async.value(r + 20), + final effect = Effect.tryFuture( + () => Future.value(10), + (error, stackTrace) => "10", ); final doing = doEffect<void, String, int>( (_) async { - final asyncEV = await _(asyncEither); - // await _(NLeft("10")); - final syncEV = await _(syncEither); - final syncV = await _(syncC); - final asyncV = await _(asyncC); final eitherV = await _(NRight(10)); - return asyncEV + syncEV + syncV + eitherV + asyncV; + final eV = await _(effect); + return eitherV + eV; }, ); diff --git a/packages/fpdart/lib/src/async.dart b/packages/fpdart/lib/src/async.dart deleted file mode 100644 index 765d354..0000000 --- a/packages/fpdart/lib/src/async.dart +++ /dev/null @@ -1,41 +0,0 @@ -part of "effect.dart"; - -final class Async<R> extends IEffect<void, Never, R> { - const Async._(UnsafeRun<void, Never, R> run) : super._(run); - - factory Async._fromEffect(IEffect<void, Never, R> effect) => - Async._(effect._runEffect); - - factory Async.make( - R Function() execute, - ) => - Async._fromEffect( - Effect<void, Never, R>.tryFuture( - execute, - (_, __) => throw Exception( - "Error when building Async.make", - ), - ), - ); - - factory Async.value(R value) => Async._( - (_) => Exit.success(value), - ); - - @override - Async<C> flatMap<C>(covariant Async<C> Function(R r) f) { - return Async._fromEffect(super.flatMap(f)); - } - - Future<R> call() async { - final run = _unsafeRun(null); - if (run is! Future) { - throw Exception("Error running a sync Async"); - } - - return switch (await run) { - Failure() => throw Exception("Invalid Async Left result"), - Success(value: final value) => value, - }; - } -} diff --git a/packages/fpdart/lib/src/async_either.dart b/packages/fpdart/lib/src/async_either.dart deleted file mode 100644 index c870ebc..0000000 --- a/packages/fpdart/lib/src/async_either.dart +++ /dev/null @@ -1,23 +0,0 @@ -part of 'effect.dart'; - -final class AsyncEither<L, R> extends IEffect<void, L, R> { - const AsyncEither._(UnsafeRun<void, L, R> run) : super._(run); - - factory AsyncEither._fromEffect(IEffect<void, L, R> effect) => - AsyncEither._(effect._runEffect); - - factory AsyncEither.tryFuture( - FutureOr<R> Function() execute, - L Function(Object error, StackTrace stackTrace) onError, - ) => - AsyncEither._fromEffect( - Effect<void, L, R>.tryFuture(execute, onError), - ); - - @override - AsyncEither<L, C> flatMap<C>(covariant AsyncEither<L, C> Function(R r) f) { - return AsyncEither._fromEffect(super.flatMap(f)); - } - - Future<Exit<L, R>> call() async => _unsafeRun(null); -} diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 8bff83e..88064dc 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -4,11 +4,7 @@ import 'package:meta/meta.dart'; import 'exit.dart'; -part 'async.dart'; -part 'async_either.dart'; part 'n_either.dart'; -part 'sync.dart'; -part 'sync_either.dart'; final class _EffectThrow<L> { final L value; @@ -57,11 +53,11 @@ abstract interface class IEffect<E, L, R> { final class Effect<E, L, R> extends IEffect<E, L, R> { const Effect._(UnsafeRun<E, L, R> run) : super._(run); - factory Effect.tryFuture( + static Effect<void, L, R> tryFuture<L, R>( FutureOr<R> Function() execute, L Function(Object error, StackTrace stackTrace) onError, ) => - Effect._( + Effect<void, L, R>._( (env) async { try { return Exit.success(await execute()); diff --git a/packages/fpdart/lib/src/sync.dart b/packages/fpdart/lib/src/sync.dart deleted file mode 100644 index e4c5827..0000000 --- a/packages/fpdart/lib/src/sync.dart +++ /dev/null @@ -1,41 +0,0 @@ -part of "effect.dart"; - -final class Sync<R> extends IEffect<void, Never, R> { - const Sync._(UnsafeRun<void, Never, R> run) : super._(run); - - factory Sync._fromEffect(IEffect<void, Never, R> effect) => - Sync._(effect._runEffect); - - factory Sync.make( - R Function() execute, - ) => - Sync._fromEffect( - Effect<void, Never, R>.tryFuture( - execute, - (_, __) => throw Exception( - "Error when building Sync.make", - ), - ), - ); - - factory Sync.value(R value) => Sync._( - (_) => Exit.success(value), - ); - - @override - Sync<C> flatMap<C>(covariant Sync<C> Function(R r) f) { - return Sync._fromEffect(super.flatMap(f)); - } - - R call() { - final run = _unsafeRun(null); - if (run is Future) { - throw Exception("Error running an async Sync"); - } - - return switch (run) { - Failure() => throw Exception("Invalid Sync Left result"), - Success(value: final value) => value, - }; - } -} diff --git a/packages/fpdart/lib/src/sync_either.dart b/packages/fpdart/lib/src/sync_either.dart deleted file mode 100644 index af84949..0000000 --- a/packages/fpdart/lib/src/sync_either.dart +++ /dev/null @@ -1,30 +0,0 @@ -part of 'effect.dart'; - -final class SyncEither<L, R> extends IEffect<void, L, R> { - const SyncEither._(UnsafeRun<void, L, R> run) : super._(run); - - factory SyncEither._fromEffect(IEffect<void, L, R> effect) => - SyncEither._(effect._runEffect); - - factory SyncEither.trySync( - R Function() execute, - L Function(Object error, StackTrace stackTrace) onError, - ) => - SyncEither._fromEffect( - Effect<void, L, R>.tryFuture(execute, onError), - ); - - @override - SyncEither<L, C> flatMap<C>(covariant SyncEither<L, C> Function(R r) f) { - return SyncEither._fromEffect(super.flatMap(f)); - } - - Exit<L, R> call() { - final run = _unsafeRun(null); - if (run is Future) { - throw Exception("Error running an async Sync"); - } - - return run; - } -} From 8980c864b56658772a39c06db569e2510de3769e Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 10 Mar 2024 16:04:27 +0900 Subject: [PATCH 07/91] `Effect` and `Either` env --- packages/fpdart/example/effect/main.dart | 13 +++--- packages/fpdart/lib/src/effect.dart | 55 ++++++++++++++++++------ packages/fpdart/lib/src/exit.dart | 10 +++++ packages/fpdart/lib/src/n_either.dart | 22 +++++++++- 4 files changed, 81 insertions(+), 19 deletions(-) diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index f786e83..cceaab7 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -1,5 +1,4 @@ import 'package:fpdart/fpdart.dart'; -import 'package:fpdart/src/effect.dart'; void main() async { final effect = Effect.tryFuture( @@ -7,16 +6,18 @@ void main() async { (error, stackTrace) => "10", ); - final doing = doEffect<void, String, int>( + final doing = doEffect<int, String, int>( (_) async { - final eitherV = await _(NRight(10)); - final eV = await _(effect); - return eitherV + eV; + final env = await _(Effect.ask()); + final beforeEnv = await _(effect.withEnv(identity)); + final mapped = await _(effect.map((r) => r + 10).withEnv(identity)); + final asEither = await _(NRight<String, int>(10).withEnv<int>()); + return beforeEnv + mapped + asEither; }, ); print(doing); - final run = await doing(null); + final run = await doing(10); print(run); } diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 88064dc..df8a440 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -13,13 +13,14 @@ final class _EffectThrow<L> { typedef DoAdapterEffect<E, L> = Future<A> Function<A>(IEffect<E, L, A>); -DoAdapterEffect<E, L> _doAdapter<E, L>(E env) => - <A>(effect) => effect._runEffect(env).then( - (exit) => switch (exit) { - Failure(value: final value) => throw _EffectThrow(value), - Success(value: final value) => value, - }, - ); +DoAdapterEffect<E, L> _doAdapter<E, L>(E env) => <A>(effect) => Future.sync( + () => effect._runEffect(env).then( + (exit) => switch (exit) { + Failure(value: final value) => throw _EffectThrow(value), + Success(value: final value) => value, + }, + ), + ); typedef DoFunctionEffect<E, L, A> = Future<A> Function(DoAdapterEffect<E, L> _); @@ -44,6 +45,14 @@ abstract interface class IEffect<E, L, R> { ), ); + @mustBeOverridden + IEffect<E, L, V> ap<V>( + IEffect<E, L, V Function(R r)> f, + ); + + @mustBeOverridden + IEffect<E, L, V> map<V>(V Function(R r) f); + @override String toString() { return "Effect(${_unsafeRun.runtimeType})"; @@ -53,11 +62,11 @@ abstract interface class IEffect<E, L, R> { final class Effect<E, L, R> extends IEffect<E, L, R> { const Effect._(UnsafeRun<E, L, R> run) : super._(run); - static Effect<void, L, R> tryFuture<L, R>( + static Effect<dynamic, L, R> tryFuture<L, R>( FutureOr<R> Function() execute, L Function(Object error, StackTrace stackTrace) onError, ) => - Effect<void, L, R>._( + Effect._( (env) async { try { return Exit.success(await execute()); @@ -67,6 +76,16 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { }, ); + factory Effect.succeed(R value) => Effect._((_) async => Exit.success(value)); + + Effect<V, L, R> withEnv<V>(E Function(V env) f) => Effect._( + (env) => _unsafeRun(f(env)), + ); + + static Effect<E, L, E> ask<E, L>() => Effect._( + (env) async => Exit.success(env), + ); + Future<Exit<L, R>> call(E env) => _runEffect(env); @override @@ -75,9 +94,21 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { } @override - Effect<E, L, C> flatMap<C>(covariant Effect<E, L, C> Function(R r) f) { - return Effect._(super.flatMap(f)._unsafeRun); - } + Effect<E, L, C> flatMap<C>(covariant Effect<E, L, C> Function(R r) f) => + Effect._(super.flatMap(f)._unsafeRun); + + @override + Effect<E, L, V> ap<V>( + covariant Effect<E, L, V Function(R r)> f, + ) => + f.flatMap( + (f) => flatMap( + (v) => Effect.succeed(f(v)), + ), + ); + + @override + Effect<E, L, V> map<V>(V Function(R r) f) => ap(Effect.succeed(f)); } Effect<E, L, R> doEffect<E, L, R>(DoFunctionEffect<E, L, R> f) => diff --git a/packages/fpdart/lib/src/exit.dart b/packages/fpdart/lib/src/exit.dart index d2a3765..3088948 100644 --- a/packages/fpdart/lib/src/exit.dart +++ b/packages/fpdart/lib/src/exit.dart @@ -7,9 +7,19 @@ sealed class Exit<L, R> { class Success<L, R> extends Exit<L, R> { final R value; const Success(this.value); + + @override + String toString() { + return "Exit.Success($value)"; + } } class Failure<L, R> extends Exit<L, R> { final L value; const Failure(this.value); + + @override + String toString() { + return "Exit.Failure($value)"; + } } diff --git a/packages/fpdart/lib/src/n_either.dart b/packages/fpdart/lib/src/n_either.dart index 62d6bcc..9fd9bbf 100644 --- a/packages/fpdart/lib/src/n_either.dart +++ b/packages/fpdart/lib/src/n_either.dart @@ -1,6 +1,6 @@ part of "effect.dart"; -sealed class NEither<L, R> extends IEffect<void, L, R> { +sealed class NEither<L, R> extends IEffect<dynamic, L, R> { const NEither._(UnsafeRun<void, L, R> run) : super._(run); @override @@ -10,6 +10,26 @@ sealed class NEither<L, R> extends IEffect<void, L, R> { NRight(value: final value) => f(value), }; } + + @override + NEither<L, V> ap<V>( + covariant NEither<L, V Function(R r)> f, + ) => + f.flatMap( + (f) => flatMap( + (v) => NRight(f(v)), + ), + ); + + @override + NEither<L, V> map<V>(V Function(R r) f) => ap(NRight(f)); + + Effect<V, L, R> withEnv<V>() => Effect._( + (env) => switch (this) { + NLeft(value: final value) => Exit.failure(value), + NRight(value: final value) => Exit.success(value), + }, + ); } // ignore: missing_override_of_must_be_overridden From f9c32f0f97d6210ae061c2e6be78ba060ec4f06c Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 10 Mar 2024 16:10:47 +0900 Subject: [PATCH 08/91] `Option` as `IEffect` --- packages/fpdart/example/effect/main.dart | 3 +- packages/fpdart/lib/src/effect.dart | 1 + packages/fpdart/lib/src/n_either.dart | 2 +- packages/fpdart/lib/src/n_option.dart | 44 ++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 packages/fpdart/lib/src/n_option.dart diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index cceaab7..6ac1216 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -12,7 +12,8 @@ void main() async { final beforeEnv = await _(effect.withEnv(identity)); final mapped = await _(effect.map((r) => r + 10).withEnv(identity)); final asEither = await _(NRight<String, int>(10).withEnv<int>()); - return beforeEnv + mapped + asEither; + final asOption = await _(NSome<int>(10).withEnv(() => "Some")); + return beforeEnv + mapped + asEither + asOption; }, ); diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index df8a440..5391957 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -5,6 +5,7 @@ import 'package:meta/meta.dart'; import 'exit.dart'; part 'n_either.dart'; +part 'n_option.dart'; final class _EffectThrow<L> { final L value; diff --git a/packages/fpdart/lib/src/n_either.dart b/packages/fpdart/lib/src/n_either.dart index 9fd9bbf..1025242 100644 --- a/packages/fpdart/lib/src/n_either.dart +++ b/packages/fpdart/lib/src/n_either.dart @@ -1,7 +1,7 @@ part of "effect.dart"; sealed class NEither<L, R> extends IEffect<dynamic, L, R> { - const NEither._(UnsafeRun<void, L, R> run) : super._(run); + const NEither._(UnsafeRun<dynamic, L, R> run) : super._(run); @override NEither<L, C> flatMap<C>(covariant NEither<L, C> Function(R r) f) { diff --git a/packages/fpdart/lib/src/n_option.dart b/packages/fpdart/lib/src/n_option.dart new file mode 100644 index 0000000..2a6dcd5 --- /dev/null +++ b/packages/fpdart/lib/src/n_option.dart @@ -0,0 +1,44 @@ +part of "effect.dart"; + +sealed class NOption<R> extends IEffect<dynamic, dynamic, R> { + const NOption._(UnsafeRun<dynamic, dynamic, R> run) : super._(run); + + @override + NOption<C> flatMap<C>(covariant NOption<C> Function(R r) f) { + return switch (this) { + NNone() => NNone<dynamic>(), + NSome(value: final value) => f(value), + }; + } + + @override + NOption<V> ap<V>( + covariant NOption<V Function(R r)> f, + ) => + f.flatMap( + (f) => flatMap( + (v) => NSome(f(v)), + ), + ); + + @override + NOption<V> map<V>(V Function(R r) f) => ap(NSome(f)); + + Effect<V, L, R> withEnv<L, V>(L Function() onNone) => Effect._( + (env) => switch (this) { + NNone() => Exit.failure(onNone()), + NSome(value: final value) => Exit.success(value), + }, + ); +} + +// ignore: missing_override_of_must_be_overridden +class NSome<R> extends NOption<R> { + final R value; + NSome(this.value) : super._((_) => Exit.success(value)); +} + +// ignore: missing_override_of_must_be_overridden +class NNone<R> extends NOption<Never> { + NNone() : super._((_) => Exit.failure(null)); +} From 0c23b7831208d41f40943166f16189332080516f Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 10 Mar 2024 16:25:14 +0900 Subject: [PATCH 09/91] `mapLeft` --- packages/fpdart/example/effect/main.dart | 8 ++++++-- packages/fpdart/lib/src/effect.dart | 12 +++++++++++- packages/fpdart/lib/src/n_either.dart | 5 +++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index 6ac1216..b90e3cb 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -1,19 +1,23 @@ import 'package:fpdart/fpdart.dart'; void main() async { - final effect = Effect.tryFuture( + final effect = Effect.tryCatch( () => Future.value(10), (error, stackTrace) => "10", ); + final effect1 = Effect.function(() => 10); + final doing = doEffect<int, String, int>( (_) async { final env = await _(Effect.ask()); final beforeEnv = await _(effect.withEnv(identity)); + final e1 = await _(effect1.mapLeft((l) => "null").withEnv(identity)); + final mapped = await _(effect.map((r) => r + 10).withEnv(identity)); final asEither = await _(NRight<String, int>(10).withEnv<int>()); final asOption = await _(NSome<int>(10).withEnv(() => "Some")); - return beforeEnv + mapped + asEither + asOption; + return beforeEnv + mapped + asEither + asOption + e1; }, ); diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 5391957..c76347c 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -63,7 +63,7 @@ abstract interface class IEffect<E, L, R> { final class Effect<E, L, R> extends IEffect<E, L, R> { const Effect._(UnsafeRun<E, L, R> run) : super._(run); - static Effect<dynamic, L, R> tryFuture<L, R>( + factory Effect.tryCatch( FutureOr<R> Function() execute, L Function(Object error, StackTrace stackTrace) onError, ) => @@ -77,6 +77,9 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { }, ); + factory Effect.function(FutureOr<R> Function() f) => + Effect._((_) async => Exit.success(await f())); + factory Effect.fail(L value) => Effect._((_) async => Exit.failure(value)); factory Effect.succeed(R value) => Effect._((_) async => Exit.success(value)); Effect<V, L, R> withEnv<V>(E Function(V env) f) => Effect._( @@ -110,6 +113,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { @override Effect<E, L, V> map<V>(V Function(R r) f) => ap(Effect.succeed(f)); + + Effect<E, C, R> mapLeft<C>(C Function(L l) f) => Effect._( + (env) async => switch ((await _runEffect(env))) { + Failure(value: final value) => Exit.failure(f(value)), + Success(value: final value) => Exit.success(value), + }, + ); } Effect<E, L, R> doEffect<E, L, R>(DoFunctionEffect<E, L, R> f) => diff --git a/packages/fpdart/lib/src/n_either.dart b/packages/fpdart/lib/src/n_either.dart index 1025242..406189c 100644 --- a/packages/fpdart/lib/src/n_either.dart +++ b/packages/fpdart/lib/src/n_either.dart @@ -30,6 +30,11 @@ sealed class NEither<L, R> extends IEffect<dynamic, L, R> { NRight(value: final value) => Exit.success(value), }, ); + + NEither<C, R> mapLeft<C>(C Function(L l) f) => switch (this) { + NLeft(value: final value) => NLeft(f(value)), + NRight(value: final value) => NRight(value), + }; } // ignore: missing_override_of_must_be_overridden From 4d2612ef2ff75cac6fde73db1fdad710773b6d79 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Wed, 13 Mar 2024 20:52:54 +0900 Subject: [PATCH 10/91] more `Effect` methods --- packages/fpdart/lib/src/effect.dart | 23 ++++++++++++++++++++--- packages/fpdart/lib/src/n_either.dart | 18 ++++++++++++++++-- packages/fpdart/lib/src/n_option.dart | 10 ++++++++-- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index c76347c..eeec486 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -33,6 +33,10 @@ abstract interface class IEffect<E, L, R> { Future<Exit<L, R>> _runEffect(E env) async => _unsafeRun(env); + @mustBeOverridden + IEffect<E, L, C> andThen<C>(IEffect<E, L, C> Function() then) => + flatMap((_) => then()); + @mustBeOverridden IEffect<E, L, C> flatMap<C>( IEffect<E, L, C> Function(R r) f, @@ -47,9 +51,7 @@ abstract interface class IEffect<E, L, R> { ); @mustBeOverridden - IEffect<E, L, V> ap<V>( - IEffect<E, L, V Function(R r)> f, - ); + IEffect<E, L, V> ap<V>(IEffect<E, L, V Function(R r)> f); @mustBeOverridden IEffect<E, L, V> map<V>(V Function(R r) f); @@ -114,12 +116,27 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { @override Effect<E, L, V> map<V>(V Function(R r) f) => ap(Effect.succeed(f)); + @override + Effect<E, L, C> andThen<C>(covariant Effect<E, L, C> Function() then) => + Effect._(super.andThen(then)._unsafeRun); + Effect<E, C, R> mapLeft<C>(C Function(L l) f) => Effect._( (env) async => switch ((await _runEffect(env))) { Failure(value: final value) => Exit.failure(f(value)), Success(value: final value) => Exit.success(value), }, ); + + Effect<E, C, R> orElse<C>( + covariant Effect<E, C, R> Function(L l) orElse, + ) => + Effect._( + (env) async => switch ((await _unsafeRun(env))) { + Failure(value: final value) => orElse(value)._unsafeRun(env), + Success(value: final value) => + Effect<E, C, R>.succeed(value)._unsafeRun(env), + }, + ); } Effect<E, L, R> doEffect<E, L, R>(DoFunctionEffect<E, L, R> f) => diff --git a/packages/fpdart/lib/src/n_either.dart b/packages/fpdart/lib/src/n_either.dart index 406189c..6d2e291 100644 --- a/packages/fpdart/lib/src/n_either.dart +++ b/packages/fpdart/lib/src/n_either.dart @@ -38,13 +38,27 @@ sealed class NEither<L, R> extends IEffect<dynamic, L, R> { } // ignore: missing_override_of_must_be_overridden -class NRight<L, R> extends NEither<L, R> { +final class NRight<L, R> extends NEither<L, R> { final R value; NRight(this.value) : super._((_) => Exit.success(value)); + + @override + NEither<L, C> andThen<C>(covariant NEither<L, C> Function() then) => then(); + + @override + NEither<C, R> orElse<C>(covariant NEither<C, R> Function(L l) orElse) => + NRight(value); } // ignore: missing_override_of_must_be_overridden -class NLeft<L, R> extends NEither<L, R> { +final class NLeft<L, R> extends NEither<L, R> { final L value; NLeft(this.value) : super._((_) => Exit.failure(value)); + + @override + NEither<L, C> andThen<C>(covariant NEither<L, C> Function() then) => + NLeft(value); + + NEither<C, R> orElse<C>(covariant NEither<C, R> Function(L l) orElse) => + orElse(value); } diff --git a/packages/fpdart/lib/src/n_option.dart b/packages/fpdart/lib/src/n_option.dart index 2a6dcd5..06480f5 100644 --- a/packages/fpdart/lib/src/n_option.dart +++ b/packages/fpdart/lib/src/n_option.dart @@ -33,12 +33,18 @@ sealed class NOption<R> extends IEffect<dynamic, dynamic, R> { } // ignore: missing_override_of_must_be_overridden -class NSome<R> extends NOption<R> { +final class NSome<R> extends NOption<R> { final R value; NSome(this.value) : super._((_) => Exit.success(value)); + + @override + NOption<C> andThen<C>(covariant NOption<C> Function() then) => then(); } // ignore: missing_override_of_must_be_overridden -class NNone<R> extends NOption<Never> { +final class NNone<R> extends NOption<Never> { NNone() : super._((_) => Exit.failure(null)); + + @override + NOption<C> andThen<C>(covariant NOption<C> Function() then) => this; } From e0c2dfa54ff3e7a3bcca53375d39464715cce03f Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 14 Mar 2024 18:02:54 +0900 Subject: [PATCH 11/91] `asEffect` for `IEffect` interface --- packages/fpdart/lib/src/effect.dart | 64 +++++++++------------------ packages/fpdart/lib/src/n_either.dart | 16 +++---- packages/fpdart/lib/src/n_option.dart | 15 +++---- 3 files changed, 34 insertions(+), 61 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index eeec486..fd3e6e3 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -1,7 +1,5 @@ import 'dart:async'; -import 'package:meta/meta.dart'; - import 'exit.dart'; part 'n_either.dart'; @@ -15,7 +13,7 @@ final class _EffectThrow<L> { typedef DoAdapterEffect<E, L> = Future<A> Function<A>(IEffect<E, L, A>); DoAdapterEffect<E, L> _doAdapter<E, L>(E env) => <A>(effect) => Future.sync( - () => effect._runEffect(env).then( + () => effect.asEffect._runEffect(env).then( (exit) => switch (exit) { Failure(value: final value) => throw _EffectThrow(value), Success(value: final value) => value, @@ -25,45 +23,27 @@ DoAdapterEffect<E, L> _doAdapter<E, L>(E env) => <A>(effect) => Future.sync( typedef DoFunctionEffect<E, L, A> = Future<A> Function(DoAdapterEffect<E, L> _); -typedef UnsafeRun<E, L, R> = FutureOr<Exit<L, R>> Function(E env); - abstract interface class IEffect<E, L, R> { - final UnsafeRun<E, L, R> _unsafeRun; - const IEffect._(this._unsafeRun); + const IEffect(); + Effect<E, L, R> get asEffect; +} - Future<Exit<L, R>> _runEffect(E env) async => _unsafeRun(env); +final class Effect<E, L, R> extends IEffect<E, L, R> { + final FutureOr<Exit<L, R>> Function(E env) _unsafeRun; - @mustBeOverridden - IEffect<E, L, C> andThen<C>(IEffect<E, L, C> Function() then) => - flatMap((_) => then()); + const Effect._(this._unsafeRun); - @mustBeOverridden - IEffect<E, L, C> flatMap<C>( - IEffect<E, L, C> Function(R r) f, - ) => - Effect._( - (env) => _runEffect(env).then( - (exit) async => switch (exit) { - Failure(value: final value) => Future.value(Exit.failure(value)), - Success(value: final value) => f(value)._runEffect(env), - }, - ), - ); + @override + Effect<E, L, R> get asEffect => this; - @mustBeOverridden - IEffect<E, L, V> ap<V>(IEffect<E, L, V Function(R r)> f); + Future<Exit<L, R>> _runEffect(E env) async => _unsafeRun(env); - @mustBeOverridden - IEffect<E, L, V> map<V>(V Function(R r) f); + Future<Exit<L, R>> call(E env) => _runEffect(env); @override String toString() { return "Effect(${_unsafeRun.runtimeType})"; } -} - -final class Effect<E, L, R> extends IEffect<E, L, R> { - const Effect._(UnsafeRun<E, L, R> run) : super._(run); factory Effect.tryCatch( FutureOr<R> Function() execute, @@ -92,18 +72,16 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) async => Exit.success(env), ); - Future<Exit<L, R>> call(E env) => _runEffect(env); - - @override - String toString() { - return "Effect(${_unsafeRun.runtimeType})"; - } - - @override Effect<E, L, C> flatMap<C>(covariant Effect<E, L, C> Function(R r) f) => - Effect._(super.flatMap(f)._unsafeRun); + Effect._( + (env) => _runEffect(env).then( + (exit) async => switch (exit) { + Failure(value: final value) => Future.value(Exit.failure(value)), + Success(value: final value) => f(value)._runEffect(env), + }, + ), + ); - @override Effect<E, L, V> ap<V>( covariant Effect<E, L, V Function(R r)> f, ) => @@ -113,12 +91,10 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); - @override Effect<E, L, V> map<V>(V Function(R r) f) => ap(Effect.succeed(f)); - @override Effect<E, L, C> andThen<C>(covariant Effect<E, L, C> Function() then) => - Effect._(super.andThen(then)._unsafeRun); + flatMap((_) => then()); Effect<E, C, R> mapLeft<C>(C Function(L l) f) => Effect._( (env) async => switch ((await _runEffect(env))) { diff --git a/packages/fpdart/lib/src/n_either.dart b/packages/fpdart/lib/src/n_either.dart index 6d2e291..a459956 100644 --- a/packages/fpdart/lib/src/n_either.dart +++ b/packages/fpdart/lib/src/n_either.dart @@ -1,9 +1,8 @@ part of "effect.dart"; sealed class NEither<L, R> extends IEffect<dynamic, L, R> { - const NEither._(UnsafeRun<dynamic, L, R> run) : super._(run); + const NEither(); - @override NEither<L, C> flatMap<C>(covariant NEither<L, C> Function(R r) f) { return switch (this) { NLeft(value: final value) => NLeft(value), @@ -11,7 +10,6 @@ sealed class NEither<L, R> extends IEffect<dynamic, L, R> { }; } - @override NEither<L, V> ap<V>( covariant NEither<L, V Function(R r)> f, ) => @@ -21,7 +19,6 @@ sealed class NEither<L, R> extends IEffect<dynamic, L, R> { ), ); - @override NEither<L, V> map<V>(V Function(R r) f) => ap(NRight(f)); Effect<V, L, R> withEnv<V>() => Effect._( @@ -37,25 +34,26 @@ sealed class NEither<L, R> extends IEffect<dynamic, L, R> { }; } -// ignore: missing_override_of_must_be_overridden final class NRight<L, R> extends NEither<L, R> { final R value; - NRight(this.value) : super._((_) => Exit.success(value)); + const NRight(this.value); @override + Effect<dynamic, L, R> get asEffect => Effect.succeed(value); + NEither<L, C> andThen<C>(covariant NEither<L, C> Function() then) => then(); - @override NEither<C, R> orElse<C>(covariant NEither<C, R> Function(L l) orElse) => NRight(value); } -// ignore: missing_override_of_must_be_overridden final class NLeft<L, R> extends NEither<L, R> { final L value; - NLeft(this.value) : super._((_) => Exit.failure(value)); + const NLeft(this.value); @override + Effect<dynamic, L, R> get asEffect => Effect.fail(value); + NEither<L, C> andThen<C>(covariant NEither<L, C> Function() then) => NLeft(value); diff --git a/packages/fpdart/lib/src/n_option.dart b/packages/fpdart/lib/src/n_option.dart index 06480f5..3b04d00 100644 --- a/packages/fpdart/lib/src/n_option.dart +++ b/packages/fpdart/lib/src/n_option.dart @@ -1,9 +1,8 @@ part of "effect.dart"; sealed class NOption<R> extends IEffect<dynamic, dynamic, R> { - const NOption._(UnsafeRun<dynamic, dynamic, R> run) : super._(run); + const NOption(); - @override NOption<C> flatMap<C>(covariant NOption<C> Function(R r) f) { return switch (this) { NNone() => NNone<dynamic>(), @@ -11,7 +10,6 @@ sealed class NOption<R> extends IEffect<dynamic, dynamic, R> { }; } - @override NOption<V> ap<V>( covariant NOption<V Function(R r)> f, ) => @@ -21,7 +19,6 @@ sealed class NOption<R> extends IEffect<dynamic, dynamic, R> { ), ); - @override NOption<V> map<V>(V Function(R r) f) => ap(NSome(f)); Effect<V, L, R> withEnv<L, V>(L Function() onNone) => Effect._( @@ -32,19 +29,21 @@ sealed class NOption<R> extends IEffect<dynamic, dynamic, R> { ); } -// ignore: missing_override_of_must_be_overridden final class NSome<R> extends NOption<R> { final R value; - NSome(this.value) : super._((_) => Exit.success(value)); + const NSome(this.value); @override + Effect<dynamic, dynamic, R> get asEffect => Effect.succeed(value); + NOption<C> andThen<C>(covariant NOption<C> Function() then) => then(); } -// ignore: missing_override_of_must_be_overridden final class NNone<R> extends NOption<Never> { - NNone() : super._((_) => Exit.failure(null)); + const NNone(); @override + Effect<dynamic, dynamic, Never> get asEffect => Effect.fail(null); + NOption<C> andThen<C>(covariant NOption<C> Function() then) => this; } From 5f945c42a737f300fdaa8fede749ceb4ddc599c9 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 14 Mar 2024 20:30:12 +0900 Subject: [PATCH 12/91] `Effect` api with categories --- packages/fpdart/example/effect/main.dart | 4 +- packages/fpdart/lib/src/effect.dart | 147 ++++++++++++++++++----- 2 files changed, 116 insertions(+), 35 deletions(-) diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index b90e3cb..257976d 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -8,11 +8,11 @@ void main() async { final effect1 = Effect.function(() => 10); - final doing = doEffect<int, String, int>( + final doing = Effect<int, String, int>.gen( (_) async { final env = await _(Effect.ask()); final beforeEnv = await _(effect.withEnv(identity)); - final e1 = await _(effect1.mapLeft((l) => "null").withEnv(identity)); + final e1 = await _(effect1.mapError((l) => "null").withEnv(identity)); final mapped = await _(effect.map((r) => r + 10).withEnv(identity)); final asEither = await _(NRight<String, int>(10).withEnv<int>()); diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index fd3e6e3..db9f8e9 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -30,21 +30,57 @@ abstract interface class IEffect<E, L, R> { final class Effect<E, L, R> extends IEffect<E, L, R> { final FutureOr<Exit<L, R>> Function(E env) _unsafeRun; - const Effect._(this._unsafeRun); @override Effect<E, L, R> get asEffect => this; + @override + String toString() { + return "Effect(${_unsafeRun.runtimeType})"; + } + + /// {@category execution} Future<Exit<L, R>> _runEffect(E env) async => _unsafeRun(env); + /// {@category execution} Future<Exit<L, R>> call(E env) => _runEffect(env); - @override - String toString() { - return "Effect(${_unsafeRun.runtimeType})"; + /// {@category execution} + R runSync(E env) { + final result = _unsafeRun(env); + if (result is Future) { + throw Exception("Cannot use runSync for an async Effect"); + } + + return switch (result) { + Failure<L, R>() => throw Exception("Failed runSync Effect"), + Success<L, R>(value: final value) => value, + }; + } + + /// {@category execution} + Future<R> runFuture(E env) async { + final result = await _unsafeRun(env); + return switch (result) { + Failure<L, R>() => throw Exception("Failed runFuture Effect"), + Success<L, R>(value: final value) => value, + }; } + /// {@category constructors} + // ignore: non_constant_identifier_names + factory Effect.gen(DoFunctionEffect<E, L, R> f) => Effect<E, L, R>._( + (env) async { + try { + return Exit.success(await f(_doAdapter<E, L>(env))); + } on _EffectThrow<L> catch (e) { + return Exit.failure(e.value); + } + }, + ); + + /// {@category constructors} factory Effect.tryCatch( FutureOr<R> Function() execute, L Function(Object error, StackTrace stackTrace) onError, @@ -59,31 +95,35 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { }, ); - factory Effect.function(FutureOr<R> Function() f) => - Effect._((_) async => Exit.success(await f())); + /// {@category constructors} + factory Effect.function(FutureOr<R> Function() f) => Effect._( + (_) async => Exit.success(await f()), + ); + + /// {@category constructors} factory Effect.fail(L value) => Effect._((_) async => Exit.failure(value)); + + /// {@category constructors} factory Effect.succeed(R value) => Effect._((_) async => Exit.success(value)); + /// {@category constructors} + static Effect<dynamic, dynamic, void> unit() => Effect._( + (_) async => Exit.success(null), + ); + + /// {@category do_notation} Effect<V, L, R> withEnv<V>(E Function(V env) f) => Effect._( (env) => _unsafeRun(f(env)), ); + /// {@category do_notation} static Effect<E, L, E> ask<E, L>() => Effect._( (env) async => Exit.success(env), ); - Effect<E, L, C> flatMap<C>(covariant Effect<E, L, C> Function(R r) f) => - Effect._( - (env) => _runEffect(env).then( - (exit) async => switch (exit) { - Failure(value: final value) => Future.value(Exit.failure(value)), - Success(value: final value) => f(value)._runEffect(env), - }, - ), - ); - + /// {@category combining} Effect<E, L, V> ap<V>( - covariant Effect<E, L, V Function(R r)> f, + Effect<E, L, V Function(R r)> f, ) => f.flatMap( (f) => flatMap( @@ -91,20 +131,60 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); + /// {@category mapping} Effect<E, L, V> map<V>(V Function(R r) f) => ap(Effect.succeed(f)); - Effect<E, L, C> andThen<C>(covariant Effect<E, L, C> Function() then) => - flatMap((_) => then()); - - Effect<E, C, R> mapLeft<C>(C Function(L l) f) => Effect._( + /// {@category mapping} + Effect<E, C, R> mapError<C>(C Function(L l) f) => Effect._( (env) async => switch ((await _runEffect(env))) { Failure(value: final value) => Exit.failure(f(value)), Success(value: final value) => Exit.success(value), }, ); + /// {@category mapping} + Effect<E, C, D> mapBoth<C, D>(C Function(L l) fl, D Function(R r) fr) => + Effect._( + (env) async => switch ((await _runEffect(env))) { + Failure(value: final value) => Exit.failure(fl(value)), + Success(value: final value) => Exit.success(fr(value)), + }, + ); + + /// {@category sequencing} + Effect<E, L, C> flatMap<C>(Effect<E, L, C> Function(R r) f) => Effect._( + (env) => _runEffect(env).then( + (exit) async => switch (exit) { + Failure(value: final value) => Future.value(Exit.failure(value)), + Success(value: final value) => f(value)._runEffect(env), + }, + ), + ); + + /// {@category sequencing} + Effect<E, L, R> tap<C>(Effect<E, L, C> Function(R r) f) => + flatMap((r) => f(r).map((_) => r)); + + /// {@category sequencing} + Effect<E, L, R> tapError<C>(Effect<E, C, R> Function(L l) f) => Effect._( + (env) async { + switch ((await _runEffect(env))) { + case Failure(value: final value): + await f(value)._unsafeRun(env); + return Exit<L, R>.failure(value); + case Success(value: final value): + return Exit.success(value); + } + }, + ); + + /// {@category sequencing} + Effect<E, L, C> andThen<C>(Effect<E, L, C> Function() then) => + flatMap((_) => then()); + + /// {@category alternatives} Effect<E, C, R> orElse<C>( - covariant Effect<E, C, R> Function(L l) orElse, + Effect<E, C, R> Function(L l) orElse, ) => Effect._( (env) async => switch ((await _unsafeRun(env))) { @@ -113,15 +193,16 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, C, R>.succeed(value)._unsafeRun(env), }, ); -} -Effect<E, L, R> doEffect<E, L, R>(DoFunctionEffect<E, L, R> f) => - Effect<E, L, R>._( - (env) async { - try { - return Exit.success(await f(_doAdapter<E, L>(env))); - } on _EffectThrow<L> catch (e) { - return Exit.failure(e.value); - } - }, - ); + /// {@category error_handling} + Effect<E, dynamic, R> catchAll( + Effect<E, dynamic, R> Function(L error) f, + ) => + Effect._( + (env) async => switch ((await _unsafeRun(env))) { + Failure(value: final value) => f(value)._unsafeRun(env), + Success(value: final value) => + Effect<dynamic, dynamic, R>.succeed(value)._unsafeRun(env), + }, + ); +} From bddc3b9893c26356eb8f6441147babbf868fa8f4 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 06:42:21 +0900 Subject: [PATCH 13/91] file system example with `Effect` --- examples/read_write_file/file_system.dart | 24 +++++++ examples/read_write_file/main.dart | 82 +++++++++++++---------- packages/fpdart/example/effect/main.dart | 2 +- packages/fpdart/lib/src/effect.dart | 8 +-- 4 files changed, 76 insertions(+), 40 deletions(-) create mode 100644 examples/read_write_file/file_system.dart diff --git a/examples/read_write_file/file_system.dart b/examples/read_write_file/file_system.dart new file mode 100644 index 0000000..e246b00 --- /dev/null +++ b/examples/read_write_file/file_system.dart @@ -0,0 +1,24 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:fpdart/fpdart.dart'; + +sealed class FileSystemError { + const FileSystemError(); +} + +class ReadFileError extends FileSystemError { + const ReadFileError(); +} + +final class FileSystem { + Effect<File, FileSystemError, List<String>> readAsLines({ + Encoding encoding = utf8, + }) => + Effect.env<File, FileSystemError>().flatMap( + (file) => Effect.tryCatch( + () async => file.readAsLines(encoding: encoding), + (error, stackTrace) => const ReadFileError(), + ), + ); +} diff --git a/examples/read_write_file/main.dart b/examples/read_write_file/main.dart index a1d364f..65da042 100644 --- a/examples/read_write_file/main.dart +++ b/examples/read_write_file/main.dart @@ -2,14 +2,13 @@ import 'dart:io'; import 'package:fpdart/fpdart.dart'; +import 'file_system.dart'; + /** - * Read lines from a `.txt` file using [TaskEither] of fpdart. + * Read lines from a `.txt` file using [Effect] of fpdart. * * This application reads from two files containing english and italian sentences. * It then uses `zip` to join the two resulting lists together in a `List<(String, String)>`. - * - * Finally, it uses `flatMap` and `foldLeft` to iterate over the sentences and search words from a predefined list (`searchWords`). - * At the end, we have a list of [FoundWord] containing all the sentences and words matched. */ /// Store words and sentence in [FoundWord] class @@ -53,48 +52,61 @@ Iterable<FoundWord> collectFoundWords( ), ); +typedef Env = ({ + FileSystem fileSystem, + File sourceIta, + File sourceEng, +}); + void main() async { - final collectDoNotation = TaskEither<String, Iterable<FoundWord>>.Do( + final collectDoNotation = + Effect<Env, FileSystemError, Iterable<FoundWord>>.gen( (_) async { - final linesIta = await _(readFileAsync('./assets/source_ita.txt')); - final linesEng = await _(readFileAsync('./assets/source_eng.txt')); + final env = await _(Effect.env()); + + final linesIta = await _( + env.fileSystem.readAsLines().withEnv( + (env) => env.sourceIta, + ), + ); + + final linesEng = await _( + env.fileSystem.readAsLines().withEnv( + (env) => env.sourceEng, + ), + ); + final linesZip = linesIta.zip(linesEng); return collectFoundWords(linesZip); }, ); - final collectFlatMap = readFileAsync('./assets/source_ita.txt') + final task = collectDoNotation .flatMap( - (linesIta) => readFileAsync('./assets/source_eng.txt').map( - (linesEng) => linesIta.zip(linesEng), + (list) => Effect.function( + () { + /// Print all the found [FoundWord] + list.forEach( + (e) => print( + '${e.index}, ${e.word}(${e.wordIndex}): ${e.english}_${e.italian}\n'), + ); + }, ), ) - .map(collectFoundWords); - - /// Read file async using [TaskEither] - /// - /// Since we are using [TaskEither], until we call the `run` method, - /// no actual reading is performed. - final task = collectDoNotation.match( - (l) => print(l), - (list) { - /// Print all the found [FoundWord] - list.forEach( - (e) => print( - '${e.index}, ${e.word}(${e.wordIndex}): ${e.english}_${e.italian}\n'), + .catchAll( + (error) => Effect.function( + () { + print(error); + }, + ), ); - }, - ); /// Run the reading process - await task.run(); + await task.runFuture( + ( + fileSystem: FileSystem(), + sourceEng: File('./assets/source_eng.txt'), + sourceIta: File('./assets/source_ita.txt'), + ), + ); } - -/// Read file content in `source` directory using [TaskEither] -/// -/// Since the operation may fail, initialize [TaskEither] using `tryCatch` -TaskEither<String, List<String>> readFileAsync(String source) => - TaskEither.tryCatch( - () async => File(source).readAsLines(), - (error, stackTrace) => 'Error opening file $source: $error', - ); diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index 257976d..6c8bea8 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -10,7 +10,7 @@ void main() async { final doing = Effect<int, String, int>.gen( (_) async { - final env = await _(Effect.ask()); + final env = await _(Effect.env()); final beforeEnv = await _(effect.withEnv(identity)); final e1 = await _(effect1.mapError((l) => "null").withEnv(identity)); diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index db9f8e9..b1298a8 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -117,7 +117,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category do_notation} - static Effect<E, L, E> ask<E, L>() => Effect._( + static Effect<E, L, E> env<E, L>() => Effect._( (env) async => Exit.success(env), ); @@ -195,14 +195,14 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category error_handling} - Effect<E, dynamic, R> catchAll( - Effect<E, dynamic, R> Function(L error) f, + Effect<E, Never, R> catchAll( + Effect<E, Never, R> Function(L error) f, ) => Effect._( (env) async => switch ((await _unsafeRun(env))) { Failure(value: final value) => f(value)._unsafeRun(env), Success(value: final value) => - Effect<dynamic, dynamic, R>.succeed(value)._unsafeRun(env), + Effect<E, Never, R>.succeed(value)._unsafeRun(env), }, ); } From a5af9a8821cb2563a313e35eba7cb5eaacfe5058 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 07:12:23 +0900 Subject: [PATCH 14/91] refactor with abstract env --- .../{ => lib}/file_system.dart | 8 +- examples/read_write_file/lib/main.dart | 23 ++++ examples/read_write_file/lib/program.dart | 85 +++++++++++++ examples/read_write_file/main.dart | 112 ------------------ 4 files changed, 115 insertions(+), 113 deletions(-) rename examples/read_write_file/{ => lib}/file_system.dart (74%) create mode 100644 examples/read_write_file/lib/main.dart create mode 100644 examples/read_write_file/lib/program.dart delete mode 100644 examples/read_write_file/main.dart diff --git a/examples/read_write_file/file_system.dart b/examples/read_write_file/lib/file_system.dart similarity index 74% rename from examples/read_write_file/file_system.dart rename to examples/read_write_file/lib/file_system.dart index e246b00..7250cd6 100644 --- a/examples/read_write_file/file_system.dart +++ b/examples/read_write_file/lib/file_system.dart @@ -11,7 +11,13 @@ class ReadFileError extends FileSystemError { const ReadFileError(); } -final class FileSystem { +abstract interface class FileSystem { + Effect<File, FileSystemError, List<String>> readAsLines({ + Encoding encoding = utf8, + }); +} + +final class FileSystemLive extends FileSystem { Effect<File, FileSystemError, List<String>> readAsLines({ Encoding encoding = utf8, }) => diff --git a/examples/read_write_file/lib/main.dart b/examples/read_write_file/lib/main.dart new file mode 100644 index 0000000..10c1c04 --- /dev/null +++ b/examples/read_write_file/lib/main.dart @@ -0,0 +1,23 @@ +import 'dart:io'; + +import 'package:fpdart/fpdart.dart'; + +import 'file_system.dart'; +import 'program.dart'; + +/** + * Read lines from a `.txt` file using [Effect] of fpdart. + * + * This application reads from two files containing english and italian sentences. + * It then uses `zip` to join the two resulting lists together in a `List<(String, String)>`. + */ + +void main() async { + await program.runFuture( + ( + fileSystem: FileSystemLive(), + sourceEng: File('./assets/source_eng.txt'), + sourceIta: File('./assets/source_ita.txt'), + ), + ); +} diff --git a/examples/read_write_file/lib/program.dart b/examples/read_write_file/lib/program.dart new file mode 100644 index 0000000..7ab6847 --- /dev/null +++ b/examples/read_write_file/lib/program.dart @@ -0,0 +1,85 @@ +import 'dart:io'; + +import 'package:fpdart/fpdart.dart'; + +import 'file_system.dart'; + +class _FoundWord { + final int index; + final String word; + final int wordIndex; + final String english; + final String italian; + const _FoundWord( + this.index, + this.word, + this.wordIndex, + this.english, + this.italian, + ); +} + +/// Word to search in each sentence +const _searchWords = ['that', 'and', 'for']; + +Iterable<_FoundWord> _collectFoundWords( + Iterable<(String, String)> iterable, +) => + iterable.flatMapWithIndex( + (tuple, index) => _searchWords.foldLeftWithIndex<List<_FoundWord>>( + [], + (acc, word, wordIndex) => + tuple.$2.toLowerCase().split(' ').contains(word) + ? [ + ...acc, + _FoundWord( + index, + word, + wordIndex, + tuple.$2.replaceAll(word, '<\$>'), + tuple.$1, + ), + ] + : acc, + ), + ); + +typedef Env = ({FileSystem fileSystem, File sourceIta, File sourceEng}); + +final program = Effect<Env, FileSystemError, Iterable<_FoundWord>>.gen( + (_) async { + final env = await _(Effect.env()); + + final linesIta = await _( + env.fileSystem.readAsLines().withEnv( + (env) => env.sourceIta, + ), + ); + + final linesEng = await _( + env.fileSystem.readAsLines().withEnv( + (env) => env.sourceEng, + ), + ); + + final linesZip = linesIta.zip(linesEng); + return _collectFoundWords(linesZip); + }, +) + .flatMap( + (list) => Effect.function( + () { + list.forEach( + (e) => print( + '${e.index}, ${e.word}(${e.wordIndex}): ${e.english}_${e.italian}\n'), + ); + }, + ), + ) + .catchAll( + (error) => Effect.function( + () { + print(error); + }, + ), + ); diff --git a/examples/read_write_file/main.dart b/examples/read_write_file/main.dart deleted file mode 100644 index 65da042..0000000 --- a/examples/read_write_file/main.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'dart:io'; - -import 'package:fpdart/fpdart.dart'; - -import 'file_system.dart'; - -/** - * Read lines from a `.txt` file using [Effect] of fpdart. - * - * This application reads from two files containing english and italian sentences. - * It then uses `zip` to join the two resulting lists together in a `List<(String, String)>`. - */ - -/// Store words and sentence in [FoundWord] class -class FoundWord { - final int index; - final String word; - final int wordIndex; - final String english; - final String italian; - const FoundWord( - this.index, - this.word, - this.wordIndex, - this.english, - this.italian, - ); -} - -/// Word to search in each sentence -const searchWords = ['that', 'and', 'for']; - -Iterable<FoundWord> collectFoundWords( - Iterable<(String, String)> iterable, -) => - iterable.flatMapWithIndex( - (tuple, index) => searchWords.foldLeftWithIndex<List<FoundWord>>( - [], - (acc, word, wordIndex) => - tuple.$2.toLowerCase().split(' ').contains(word) - ? [ - ...acc, - FoundWord( - index, - word, - wordIndex, - tuple.$2.replaceAll(word, '<\$>'), - tuple.$1, - ), - ] - : acc, - ), - ); - -typedef Env = ({ - FileSystem fileSystem, - File sourceIta, - File sourceEng, -}); - -void main() async { - final collectDoNotation = - Effect<Env, FileSystemError, Iterable<FoundWord>>.gen( - (_) async { - final env = await _(Effect.env()); - - final linesIta = await _( - env.fileSystem.readAsLines().withEnv( - (env) => env.sourceIta, - ), - ); - - final linesEng = await _( - env.fileSystem.readAsLines().withEnv( - (env) => env.sourceEng, - ), - ); - - final linesZip = linesIta.zip(linesEng); - return collectFoundWords(linesZip); - }, - ); - - final task = collectDoNotation - .flatMap( - (list) => Effect.function( - () { - /// Print all the found [FoundWord] - list.forEach( - (e) => print( - '${e.index}, ${e.word}(${e.wordIndex}): ${e.english}_${e.italian}\n'), - ); - }, - ), - ) - .catchAll( - (error) => Effect.function( - () { - print(error); - }, - ), - ); - - /// Run the reading process - await task.runFuture( - ( - fileSystem: FileSystem(), - sourceEng: File('./assets/source_eng.txt'), - sourceIta: File('./assets/source_ita.txt'), - ), - ); -} From 8e3aa09d961167408fae6ccb5863810693ecb362 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 07:34:25 +0900 Subject: [PATCH 15/91] test with `provide` in `Effect` --- examples/read_write_file/lib/file_system.dart | 2 +- examples/read_write_file/lib/main.dart | 22 ++++++++++- examples/read_write_file/lib/program.dart | 38 ++++++------------- examples/read_write_file/pubspec.yaml | 1 + examples/read_write_file/test/main_test.dart | 34 +++++++++++++++++ packages/fpdart/example/effect/main.dart | 10 ++--- packages/fpdart/lib/src/effect.dart | 8 +++- packages/fpdart/lib/src/n_either.dart | 8 ++-- packages/fpdart/lib/src/n_option.dart | 14 ++++--- 9 files changed, 93 insertions(+), 44 deletions(-) create mode 100644 examples/read_write_file/test/main_test.dart diff --git a/examples/read_write_file/lib/file_system.dart b/examples/read_write_file/lib/file_system.dart index 7250cd6..f13db9e 100644 --- a/examples/read_write_file/lib/file_system.dart +++ b/examples/read_write_file/lib/file_system.dart @@ -11,7 +11,7 @@ class ReadFileError extends FileSystemError { const ReadFileError(); } -abstract interface class FileSystem { +abstract class FileSystem { Effect<File, FileSystemError, List<String>> readAsLines({ Encoding encoding = utf8, }); diff --git a/examples/read_write_file/lib/main.dart b/examples/read_write_file/lib/main.dart index 10c1c04..4d902ff 100644 --- a/examples/read_write_file/lib/main.dart +++ b/examples/read_write_file/lib/main.dart @@ -13,8 +13,28 @@ import 'program.dart'; */ void main() async { - await program.runFuture( + final main = program + .flatMap( + (list) => Effect.function( + () { + list.forEach( + (e) => print( + '${e.index}, ${e.word}(${e.wordIndex}): ${e.english}_${e.italian}\n'), + ); + }, + ), + ) + .catchAll( + (error) => Effect.function( + () { + print(error); + }, + ), + ); + + await main.runFuture( ( + searchWords: const ['that', 'and', 'for'], fileSystem: FileSystemLive(), sourceEng: File('./assets/source_eng.txt'), sourceIta: File('./assets/source_ita.txt'), diff --git a/examples/read_write_file/lib/program.dart b/examples/read_write_file/lib/program.dart index 7ab6847..b0d1589 100644 --- a/examples/read_write_file/lib/program.dart +++ b/examples/read_write_file/lib/program.dart @@ -19,14 +19,12 @@ class _FoundWord { ); } -/// Word to search in each sentence -const _searchWords = ['that', 'and', 'for']; - Iterable<_FoundWord> _collectFoundWords( + List<String> searchWords, Iterable<(String, String)> iterable, ) => iterable.flatMapWithIndex( - (tuple, index) => _searchWords.foldLeftWithIndex<List<_FoundWord>>( + (tuple, index) => searchWords.foldLeftWithIndex<List<_FoundWord>>( [], (acc, word, wordIndex) => tuple.$2.toLowerCase().split(' ').contains(word) @@ -44,42 +42,30 @@ Iterable<_FoundWord> _collectFoundWords( ), ); -typedef Env = ({FileSystem fileSystem, File sourceIta, File sourceEng}); +typedef Env = ({ + FileSystem fileSystem, + File sourceIta, + File sourceEng, + List<String> searchWords, +}); final program = Effect<Env, FileSystemError, Iterable<_FoundWord>>.gen( (_) async { final env = await _(Effect.env()); final linesIta = await _( - env.fileSystem.readAsLines().withEnv( + env.fileSystem.readAsLines().provide( (env) => env.sourceIta, ), ); final linesEng = await _( - env.fileSystem.readAsLines().withEnv( + env.fileSystem.readAsLines().provide( (env) => env.sourceEng, ), ); final linesZip = linesIta.zip(linesEng); - return _collectFoundWords(linesZip); + return _collectFoundWords(env.searchWords, linesZip); }, -) - .flatMap( - (list) => Effect.function( - () { - list.forEach( - (e) => print( - '${e.index}, ${e.word}(${e.wordIndex}): ${e.english}_${e.italian}\n'), - ); - }, - ), - ) - .catchAll( - (error) => Effect.function( - () { - print(error); - }, - ), - ); +); diff --git a/examples/read_write_file/pubspec.yaml b/examples/read_write_file/pubspec.yaml index 80ea3f8..084e925 100644 --- a/examples/read_write_file/pubspec.yaml +++ b/examples/read_write_file/pubspec.yaml @@ -15,3 +15,4 @@ dependencies: dev_dependencies: lint: ^1.5.3 + test: ^1.25.2 diff --git a/examples/read_write_file/test/main_test.dart b/examples/read_write_file/test/main_test.dart new file mode 100644 index 0000000..8790150 --- /dev/null +++ b/examples/read_write_file/test/main_test.dart @@ -0,0 +1,34 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:fpdart/src/effect.dart'; +import 'package:fpdart_read_write_file/file_system.dart'; +import 'package:fpdart_read_write_file/program.dart'; +import 'package:test/test.dart'; + +final class FileSystemTest extends FileSystem { + @override + Effect<File, FileSystemError, List<String>> readAsLines( + {Encoding encoding = utf8}) => + Effect.succeed( + ["a sentence here"], + ); +} + +void main() { + test('program', () async { + final result = await program.runFuture( + ( + searchWords: const ['sentence'], + fileSystem: FileSystemTest(), + sourceEng: File(''), + sourceIta: File(''), + ), + ); + + expect( + result.map((e) => e.english).toList(), + ['a <\$> here'], + ); + }); +} diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index 6c8bea8..b35a40b 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -11,12 +11,12 @@ void main() async { final doing = Effect<int, String, int>.gen( (_) async { final env = await _(Effect.env()); - final beforeEnv = await _(effect.withEnv(identity)); - final e1 = await _(effect1.mapError((l) => "null").withEnv(identity)); + final beforeEnv = await _(effect.provide(identity)); + final e1 = await _(effect1.mapError((l) => "null").provide(identity)); - final mapped = await _(effect.map((r) => r + 10).withEnv(identity)); - final asEither = await _(NRight<String, int>(10).withEnv<int>()); - final asOption = await _(NSome<int>(10).withEnv(() => "Some")); + final mapped = await _(effect.map((r) => r + 10).provide(identity)); + final asEither = await _(NRight<String, int>(10).provide<int>()); + final asOption = await _(NSome<int>(10).provide(() => "Some")); return beforeEnv + mapped + asEither + asOption + e1; }, ); diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index b1298a8..cca4a89 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'package:meta/meta.dart'; + import 'exit.dart'; part 'n_either.dart'; @@ -107,12 +109,14 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { factory Effect.succeed(R value) => Effect._((_) async => Exit.success(value)); /// {@category constructors} - static Effect<dynamic, dynamic, void> unit() => Effect._( + static Effect<Never, Never, void> unit() => Effect._( (_) async => Exit.success(null), ); + /// Extract the required dependency from the complete environment. + /// /// {@category do_notation} - Effect<V, L, R> withEnv<V>(E Function(V env) f) => Effect._( + Effect<V, L, R> provide<V>(E Function(V env) f) => Effect._( (env) => _unsafeRun(f(env)), ); diff --git a/packages/fpdart/lib/src/n_either.dart b/packages/fpdart/lib/src/n_either.dart index a459956..dc9a7b8 100644 --- a/packages/fpdart/lib/src/n_either.dart +++ b/packages/fpdart/lib/src/n_either.dart @@ -1,6 +1,6 @@ part of "effect.dart"; -sealed class NEither<L, R> extends IEffect<dynamic, L, R> { +sealed class NEither<L, R> extends IEffect<Never, L, R> { const NEither(); NEither<L, C> flatMap<C>(covariant NEither<L, C> Function(R r) f) { @@ -21,7 +21,7 @@ sealed class NEither<L, R> extends IEffect<dynamic, L, R> { NEither<L, V> map<V>(V Function(R r) f) => ap(NRight(f)); - Effect<V, L, R> withEnv<V>() => Effect._( + Effect<V, L, R> provide<V>() => Effect._( (env) => switch (this) { NLeft(value: final value) => Exit.failure(value), NRight(value: final value) => Exit.success(value), @@ -39,7 +39,7 @@ final class NRight<L, R> extends NEither<L, R> { const NRight(this.value); @override - Effect<dynamic, L, R> get asEffect => Effect.succeed(value); + Effect<Never, L, R> get asEffect => Effect.succeed(value); NEither<L, C> andThen<C>(covariant NEither<L, C> Function() then) => then(); @@ -52,7 +52,7 @@ final class NLeft<L, R> extends NEither<L, R> { const NLeft(this.value); @override - Effect<dynamic, L, R> get asEffect => Effect.fail(value); + Effect<Never, L, R> get asEffect => Effect.fail(value); NEither<L, C> andThen<C>(covariant NEither<L, C> Function() then) => NLeft(value); diff --git a/packages/fpdart/lib/src/n_option.dart b/packages/fpdart/lib/src/n_option.dart index 3b04d00..6bad6b0 100644 --- a/packages/fpdart/lib/src/n_option.dart +++ b/packages/fpdart/lib/src/n_option.dart @@ -1,11 +1,11 @@ part of "effect.dart"; -sealed class NOption<R> extends IEffect<dynamic, dynamic, R> { +sealed class NOption<R> extends IEffect<Never, Never, R> { const NOption(); NOption<C> flatMap<C>(covariant NOption<C> Function(R r) f) { return switch (this) { - NNone() => NNone<dynamic>(), + NNone() => NNone<Never>(), NSome(value: final value) => f(value), }; } @@ -21,7 +21,7 @@ sealed class NOption<R> extends IEffect<dynamic, dynamic, R> { NOption<V> map<V>(V Function(R r) f) => ap(NSome(f)); - Effect<V, L, R> withEnv<L, V>(L Function() onNone) => Effect._( + Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect._( (env) => switch (this) { NNone() => Exit.failure(onNone()), NSome(value: final value) => Exit.success(value), @@ -34,7 +34,7 @@ final class NSome<R> extends NOption<R> { const NSome(this.value); @override - Effect<dynamic, dynamic, R> get asEffect => Effect.succeed(value); + Effect<Never, Never, R> get asEffect => Effect.succeed(value); NOption<C> andThen<C>(covariant NOption<C> Function() then) => then(); } @@ -43,7 +43,11 @@ final class NNone<R> extends NOption<Never> { const NNone(); @override - Effect<dynamic, dynamic, Never> get asEffect => Effect.fail(null); + @internal + + /// **This will always throw, don't use it!** + // ignore: cast_from_null_always_fails + Effect<Never, Never, Never> get asEffect => Effect.fail(null as Never); NOption<C> andThen<C>(covariant NOption<C> Function() then) => this; } From 9d42ff622331846ca2f5b8adf7d654f1f2f1681c Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 09:39:34 +0900 Subject: [PATCH 16/91] `catchError` --- examples/read_write_file/lib/main.dart | 2 +- packages/fpdart/lib/src/effect.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/read_write_file/lib/main.dart b/examples/read_write_file/lib/main.dart index 4d902ff..0dce251 100644 --- a/examples/read_write_file/lib/main.dart +++ b/examples/read_write_file/lib/main.dart @@ -24,7 +24,7 @@ void main() async { }, ), ) - .catchAll( + .catchError( (error) => Effect.function( () { print(error); diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index cca4a89..63aaac3 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -199,7 +199,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category error_handling} - Effect<E, Never, R> catchAll( + Effect<E, Never, R> catchError( Effect<E, Never, R> Function(L error) f, ) => Effect._( From ae2a3ef4e5c42dd8f10efa8cf18b3e75fa919e14 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 10:31:33 +0900 Subject: [PATCH 17/91] refactor internals --- packages/fpdart/example/effect/main.dart | 4 +- packages/fpdart/lib/fpdart.dart | 20 +- packages/fpdart/lib/src/date.dart | 13 - packages/fpdart/lib/src/effect.dart | 4 +- packages/fpdart/lib/src/either.dart | 691 +----------------- .../src/extension/date_time_extension.dart | 17 - .../lib/src/extension/extension.export.dart | 8 - .../lib/src/extension/iterable_extension.dart | 72 +- .../lib/src/extension/list_extension.dart | 223 ------ .../lib/src/extension/map_extension.dart | 155 +--- .../lib/src/extension/option_extension.dart | 30 - .../src/extension/predicate_extension.dart | 51 -- .../lib/src/extension/string_extension.dart | 34 - packages/fpdart/lib/src/function.dart | 48 -- packages/fpdart/lib/src/io.dart | 150 ---- packages/fpdart/lib/src/io_either.dart | 287 -------- packages/fpdart/lib/src/io_option.dart | 258 ------- packages/fpdart/lib/src/io_ref.dart | 71 -- packages/fpdart/lib/src/n_either.dart | 62 -- packages/fpdart/lib/src/n_option.dart | 53 -- packages/fpdart/lib/src/option.dart | 624 +--------------- packages/fpdart/lib/src/order.dart | 61 ++ packages/fpdart/lib/src/ordering.dart | 21 + packages/fpdart/lib/src/random.dart | 19 - packages/fpdart/lib/src/reader.dart | 97 --- packages/fpdart/lib/src/reader_task.dart | 145 ---- .../fpdart/lib/src/reader_task_either.dart | 371 ---------- packages/fpdart/lib/src/state.dart | 159 ---- packages/fpdart/lib/src/state_async.dart | 132 ---- packages/fpdart/lib/src/task.dart | 190 ----- packages/fpdart/lib/src/task_either.dart | 387 ---------- packages/fpdart/lib/src/task_option.dart | 313 -------- packages/fpdart/lib/src/typeclass/alt.dart | 18 - .../fpdart/lib/src/typeclass/applicative.dart | 26 - packages/fpdart/lib/src/typeclass/band.dart | 31 - .../src/typeclass/bounded_semilattice.dart | 62 -- .../lib/src/typeclass/commutative_group.dart | 40 - .../lib/src/typeclass/commutative_monoid.dart | 37 - .../src/typeclass/commutative_semigroup.dart | 21 - packages/fpdart/lib/src/typeclass/eq.dart | 131 ---- packages/fpdart/lib/src/typeclass/extend.dart | 22 - .../fpdart/lib/src/typeclass/filterable.dart | 20 - .../fpdart/lib/src/typeclass/foldable.dart | 76 -- .../fpdart/lib/src/typeclass/functor.dart | 17 - packages/fpdart/lib/src/typeclass/group.dart | 58 -- packages/fpdart/lib/src/typeclass/hash.dart | 29 - packages/fpdart/lib/src/typeclass/hkt.dart | 20 - packages/fpdart/lib/src/typeclass/monad.dart | 96 --- packages/fpdart/lib/src/typeclass/monoid.dart | 81 -- packages/fpdart/lib/src/typeclass/order.dart | 151 ---- .../lib/src/typeclass/partial_order.dart | 95 --- .../fpdart/lib/src/typeclass/semigroup.dart | 72 -- .../fpdart/lib/src/typeclass/semilattice.dart | 82 --- .../lib/src/typeclass/typeclass.export.dart | 21 - packages/fpdart/lib/src/typedef.dart | 4 - 55 files changed, 197 insertions(+), 5783 deletions(-) delete mode 100644 packages/fpdart/lib/src/date.dart delete mode 100644 packages/fpdart/lib/src/extension/date_time_extension.dart delete mode 100644 packages/fpdart/lib/src/extension/extension.export.dart delete mode 100644 packages/fpdart/lib/src/extension/option_extension.dart delete mode 100644 packages/fpdart/lib/src/extension/predicate_extension.dart delete mode 100644 packages/fpdart/lib/src/extension/string_extension.dart delete mode 100644 packages/fpdart/lib/src/io.dart delete mode 100644 packages/fpdart/lib/src/io_either.dart delete mode 100644 packages/fpdart/lib/src/io_option.dart delete mode 100644 packages/fpdart/lib/src/io_ref.dart delete mode 100644 packages/fpdart/lib/src/n_either.dart delete mode 100644 packages/fpdart/lib/src/n_option.dart create mode 100644 packages/fpdart/lib/src/order.dart create mode 100644 packages/fpdart/lib/src/ordering.dart delete mode 100644 packages/fpdart/lib/src/random.dart delete mode 100644 packages/fpdart/lib/src/reader.dart delete mode 100644 packages/fpdart/lib/src/reader_task.dart delete mode 100644 packages/fpdart/lib/src/reader_task_either.dart delete mode 100644 packages/fpdart/lib/src/state.dart delete mode 100644 packages/fpdart/lib/src/state_async.dart delete mode 100644 packages/fpdart/lib/src/task.dart delete mode 100644 packages/fpdart/lib/src/task_either.dart delete mode 100644 packages/fpdart/lib/src/task_option.dart delete mode 100644 packages/fpdart/lib/src/typeclass/alt.dart delete mode 100644 packages/fpdart/lib/src/typeclass/applicative.dart delete mode 100644 packages/fpdart/lib/src/typeclass/band.dart delete mode 100644 packages/fpdart/lib/src/typeclass/bounded_semilattice.dart delete mode 100644 packages/fpdart/lib/src/typeclass/commutative_group.dart delete mode 100644 packages/fpdart/lib/src/typeclass/commutative_monoid.dart delete mode 100644 packages/fpdart/lib/src/typeclass/commutative_semigroup.dart delete mode 100644 packages/fpdart/lib/src/typeclass/eq.dart delete mode 100644 packages/fpdart/lib/src/typeclass/extend.dart delete mode 100644 packages/fpdart/lib/src/typeclass/filterable.dart delete mode 100644 packages/fpdart/lib/src/typeclass/foldable.dart delete mode 100644 packages/fpdart/lib/src/typeclass/functor.dart delete mode 100644 packages/fpdart/lib/src/typeclass/group.dart delete mode 100644 packages/fpdart/lib/src/typeclass/hash.dart delete mode 100644 packages/fpdart/lib/src/typeclass/hkt.dart delete mode 100644 packages/fpdart/lib/src/typeclass/monad.dart delete mode 100644 packages/fpdart/lib/src/typeclass/monoid.dart delete mode 100644 packages/fpdart/lib/src/typeclass/order.dart delete mode 100644 packages/fpdart/lib/src/typeclass/partial_order.dart delete mode 100644 packages/fpdart/lib/src/typeclass/semigroup.dart delete mode 100644 packages/fpdart/lib/src/typeclass/semilattice.dart delete mode 100644 packages/fpdart/lib/src/typeclass/typeclass.export.dart delete mode 100644 packages/fpdart/lib/src/typedef.dart diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index b35a40b..8ce79f4 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -15,8 +15,8 @@ void main() async { final e1 = await _(effect1.mapError((l) => "null").provide(identity)); final mapped = await _(effect.map((r) => r + 10).provide(identity)); - final asEither = await _(NRight<String, int>(10).provide<int>()); - final asOption = await _(NSome<int>(10).provide(() => "Some")); + final asEither = await _(Right<String, int>(10).provide<int>()); + final asOption = await _(Some<int>(10).provide(() => "Some")); return beforeEnv + mapped + asEither + asOption + e1; }, ); diff --git a/packages/fpdart/lib/fpdart.dart b/packages/fpdart/lib/fpdart.dart index e366ab3..af6e037 100644 --- a/packages/fpdart/lib/fpdart.dart +++ b/packages/fpdart/lib/fpdart.dart @@ -1,22 +1,4 @@ -export 'src/date.dart'; export 'src/effect.dart'; -export 'src/either.dart'; -export 'src/extension/extension.export.dart'; +export 'src/exit.dart'; export 'src/function.dart'; -export 'src/io.dart'; -export 'src/io_either.dart'; -export 'src/io_option.dart'; -export 'src/io_ref.dart'; -export 'src/option.dart'; -export 'src/random.dart'; -export 'src/reader.dart'; -export 'src/reader_task.dart'; -export 'src/reader_task_either.dart'; -export 'src/state.dart'; -export 'src/state_async.dart'; -export 'src/task.dart'; -export 'src/task_either.dart'; -export 'src/task_option.dart'; -export 'src/typeclass/typeclass.export.dart'; -export 'src/typedef.dart'; export 'src/unit.dart'; diff --git a/packages/fpdart/lib/src/date.dart b/packages/fpdart/lib/src/date.dart deleted file mode 100644 index 5d6ce63..0000000 --- a/packages/fpdart/lib/src/date.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'io.dart'; - -/// Constructs a [DateTime] instance with current date and time in the local time zone. -/// -/// [IO] wrapper around dart `DateTime.now()`. -IO<DateTime> get dateNow => IO(() => DateTime.now()); - -/// The number of milliseconds since the "Unix epoch" 1970-01-01T00:00:00Z (UTC). -/// -/// This value is independent of the time zone. -/// -/// [IO] wrapper around dart `DateTime.now().millisecondsSinceEpoch`. -IO<int> get now => dateNow.map((date) => date.millisecondsSinceEpoch); diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 63aaac3..376c05d 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -4,8 +4,8 @@ import 'package:meta/meta.dart'; import 'exit.dart'; -part 'n_either.dart'; -part 'n_option.dart'; +part 'either.dart'; +part 'option.dart'; final class _EffectThrow<L> { final L value; diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index c3140e2..51b7a63 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -1,675 +1,62 @@ -import 'function.dart'; -import 'io_either.dart'; -import 'option.dart'; -import 'task_either.dart'; -import 'typeclass/typeclass.export.dart'; -import 'typedef.dart'; +part of "effect.dart"; -/// Return a `Right(r)`. -/// -/// Shortcut for `Either.of(r)`. -Either<L, R> right<L, R>(R r) => Right<L, R>(r); - -/// Return a `Left(l)`. -/// -/// Shortcut for `Either.left(l)`. -Either<L, R> left<L, R>(L l) => Left<L, R>(l); - -final class _EitherThrow<L> { - final L value; - const _EitherThrow(this.value); -} - -typedef DoAdapterEither<L> = R Function<R>(Either<L, R>); -DoAdapterEither<L> _doAdapter<L>() => - <R>(Either<L, R> either) => either.getOrElse( - (l) => throw _EitherThrow(l), - ); - -typedef DoFunctionEither<L, R> = R Function(DoAdapterEither<L> $); - -/// Tag the [HKT2] interface for the actual [Either]. -abstract final class _EitherHKT {} - -/// Represents a value of one of two possible types, [Left] or [Right]. -/// -/// [Either] is commonly used to **handle errors**. Instead of returning placeholder -/// values when a computation may fail (such as `-1`, `null`, etc.), we return an instance -/// of [Right] containing the correct result when a computation is successful, otherwise we return -/// an instance of [Left] containing information about the kind of error that occurred. -sealed class Either<L, R> extends HKT2<_EitherHKT, L, R> - with - Functor2<_EitherHKT, L, R>, - Applicative2<_EitherHKT, L, R>, - Monad2<_EitherHKT, L, R>, - Foldable2<_EitherHKT, L, R>, - Alt2<_EitherHKT, L, R>, - Extend2<_EitherHKT, L, R> { +sealed class Either<L, R> extends IEffect<Never, L, R> { const Either(); - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory Either.Do(DoFunctionEither<L, R> f) { - try { - return Either.of(f(_doAdapter<L>())); - } on _EitherThrow<L> catch (e) { - return Either.left(e.value); - } + Either<L, C> flatMap<C>(covariant Either<L, C> Function(R r) f) { + return switch (this) { + Left(value: final value) => Left(value), + Right(value: final value) => f(value), + }; } - /// Return the result of `f` called with `b` and the value of [Right]. - /// If this [Either] is [Left], return `b`. - @override - C foldRight<C>(C b, C Function(C acc, R b) f); - - /// Return the result of `f` called with `b` and the value of [Right]. - /// If this [Either] is [Left], return `b`. - @override - C foldLeft<C>(C b, C Function(C acc, R b) f) => - foldMap<Endo<C>>(dualEndoMonoid(), (b) => (C c) => f(c, b))(b); - - /// Use `monoid` to combine the value of [Right] applied to `f`. - @override - C foldMap<C>(Monoid<C> monoid, C Function(R b) f) => - foldRight(monoid.empty, (c, b) => monoid.combine(f(b), c)); - - /// Return the result of `f` called with `b` and the value of [Right]. - /// If this [Either] is [Left], return `b`. - @override - C foldRightWithIndex<C>(C c, C Function(int i, C acc, R b) f) => - foldRight<(C, int)>( - (c, length() - 1), - (t, b) => (f(t.$2, t.$1, b), t.$2 - 1), - ).$1; - - /// Return the result of `f` called with `b` and the value of [Right]. - /// If this [Either] is [Left], return `b`. - @override - C foldLeftWithIndex<C>(C c, C Function(int i, C acc, R b) f) => - foldLeft<(C, int)>( - (c, 0), - (t, b) => (f(t.$2, t.$1, b), t.$2 + 1), - ).$1; - - /// Returns `1` when [Either] is [Right], `0` otherwise. - @override - int length() => foldLeft(0, (b, _) => b + 1); - - /// Return the result of `predicate` applied to the value of [Right]. - /// If the [Either] is [Left], returns `false`. - @override - bool any(bool Function(R a) predicate) => foldMap(boolOrMonoid(), predicate); - - /// Return the result of `predicate` applied to the value of [Right]. - /// If the [Either] is [Left], returns `true`. - @override - bool all(bool Function(R a) predicate) => foldMap(boolAndMonoid(), predicate); - - /// Use `monoid` to combine the value of [Right]. - @override - R concatenate(Monoid<R> monoid) => foldMap(monoid, identity); - - /// If the [Either] is [Right], then change its value from type `R` to - /// type `C` using function `f`. - @override - Either<L, C> map<C>(C Function(R a) f); - - /// Define two functions to change both the [Left] and [Right] value of the - /// [Either]. - /// - /// {@template fpdart_bimap_either} - /// Same as `map`+`mapLeft` but for both [Left] and [Right] - /// (`map` is only to change [Right], while `mapLeft` is only to change [Left]). - /// {@endtemplate} - Either<C, D> bimap<C, D>(C Function(L l) mLeft, D Function(R b) mRight) => - mapLeft(mLeft).map(mRight); - - /// Return a [Right] containing the value `c`. - @override - Either<L, C> pure<C>(C c) => Right<L, C>(c); - - /// Apply the function contained inside `a` to change the value on the [Right] from - /// type `R` to a value of type `C`. - @override - Either<L, C> ap<C>(covariant Either<L, C Function(R r)> a) => - a.flatMap((f) => map(f)); - - /// If this [Either] is a [Right], then return the result of calling `then`. - /// Otherwise return [Left]. - @override - Either<L, R2> andThen<R2>(covariant Either<L, R2> Function() then) => - flatMap((_) => then()); - - /// Return the current [Either] if it is a [Right], otherwise return the result of `orElse`. - /// - /// Used to provide an **alt**ernative [Either] in case the current one is [Left]. - @override - Either<L, R> alt(covariant Either<L, R> Function() orElse); - - /// Change type of this [Either] based on its value of type `R` and the - /// value of type `C` of another [Either]. - @override - Either<L, D> map2<C, D>(covariant Either<L, C> m1, D Function(R b, C c) f) => - flatMap((b) => m1.map((c) => f(b, c))); - - /// Change type of this [Either] based on its value of type `R`, the - /// value of type `C` of a second [Either], and the value of type `D` - /// of a third [Either]. - @override - Either<L, E> map3<C, D, E>(covariant Either<L, C> m1, - covariant Either<L, D> m2, E Function(R b, C c, D d) f) => - flatMap((b) => m1.flatMap((c) => m2.map((d) => f(b, c, d)))); - - /// Change the value of [Either] from type `R` to type `Z` based on the - /// value of `Either<L, R>` using function `f`. - @override - Either<L, Z> extend<Z>(Z Function(Either<L, R> t) f); - - /// Wrap this [Either] inside another [Either]. - @override - Either<L, Either<L, R>> duplicate() => extend(identity); - - /// Chain multiple functions having the same left type `L`. - @override - Either<L, B> call<B>(covariant Either<L, B> chain) => flatMap((_) => chain); - - /// {@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<L, C> flatMap<C>(covariant Either<L, C> Function(R a) f); - - /// {@macro fpdart_flat_map_either} - /// - /// Same as `flatMap`. - Either<L, R2> bind<R2>(Either<L, R2> 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<L, R> 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 - Either<L, R> chainFirst<C>( - covariant Either<L, C> Function(R b) chain, - ) => - flatMap((b) => chain(b).map((c) => b).orElse((l) => right(b))); - - /// Used to chain multiple functions that return a `Future<Either>`. - /// - /// 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<L, R2> bindFuture<R2>(Future<Either<L, R2>> Function(R r) f); - - /// If the [Either] is [Left], then change its value from type `L` to - /// type `C` using function `f`. - Either<C, R> mapLeft<C>(C Function(L a) f); - - /// Convert this [Either] to a [Option]: - /// - If the [Either] is [Left], throw away its value and just return [None] - /// - If the [Either] is [Right], return a [Some] containing the value inside [Right] - Option<R> toOption(); - - /// Convert this [Either] to a [IOEither]. - IOEither<L, R> toIOEither(); - - /// Convert this [Either] to a [TaskEither]. - /// - /// Used to convert a sync context ([Either]) to an async context ([TaskEither]). - /// You should convert [Either] to [TaskEither] every time you need to - /// call an async ([Future]) function based on the value in [Either]. - TaskEither<L, R> toTaskEither(); - - /// Convert [Either] to nullable `R?`. - /// - /// **Note**: this loses information about a possible [Left] value, - /// converting it to simply `null`. - R? toNullable(); - - /// Return `true` when this [Either] is [Left]. - bool isLeft(); - - /// Return `true` when this [Either] is [Right]. - bool isRight(); - - /// Extract the value from [Left] in a [Option]. - /// - /// If the [Either] is [Right], return [None]. - Option<L> getLeft(); - - /// Extract the value from [Right] in a [Option]. - /// - /// If the [Either] is [Left], return [None]. - /// - /// Same as `toOption`. - Option<R> getRight() => toOption(); - - /// Swap the values contained inside the [Left] and [Right] of this [Either]. - Either<R, L> swap(); - - /// If this [Either] is [Left], return the result of `onLeft`. - /// - /// Used to recover from errors, so that when this value is [Left] you - /// try another function that returns an [Either]. - Either<L1, R> orElse<L1>(Either<L1, R> Function(L l) onLeft); - - /// Return the value inside this [Either] if it is a [Right]. - /// Otherwise return result of `onElse`. - R getOrElse(R Function(L l) orElse); - - /// Execute `onLeft` when value is [Left], otherwise execute `onRight`. - /// - /// Same as `fold`. - C match<C>(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>(C Function(L l) onLeft, C Function(R r) onRight) => - match<C>(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<R> eq); - - /// Return the result of calliing `predicate` with the value of [Either] if it is a [Right]. - /// Otherwise return `false`. - bool exists(bool Function(R r) predicate); - - /// {@template fpdart_traverse_list_either} - /// Map each element in the list to an [Either] using the function `f`, - /// and collect the result in an `Either<E, List<B>>`. - /// - /// If any mapped element of the list is [Left], then the final result - /// will be [Left]. - /// {@endtemplate} - /// - /// Same as `Either.traverseList` but passing `index` in the map function. - static Either<E, List<B>> traverseListWithIndex<E, A, B>( - List<A> list, - Either<E, B> Function(A a, int i) f, - ) { - final resultList = <B>[]; - for (var i = 0; i < list.length; i++) { - final e = f(list[i], i); - if (e is Left<E, B>) { - return left(e._value); - } else if (e is Right<E, B>) { - resultList.add(e._value); - } else { - throw Exception( - "[fpdart]: Error when mapping Either, it should be either Left or Right.", - ); - } - } - - return right(resultList); - } - - /// {@macro fpdart_traverse_list_either} - /// - /// Same as `Either.traverseListWithIndex` but without `index` in the map function. - static Either<E, List<B>> traverseList<E, A, B>( - List<A> list, - Either<E, B> Function(A a) f, - ) => - traverseListWithIndex<E, A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_either} - /// Convert a `List<Either<E, A>>` to a single `Either<E, List<A>>`. - /// - /// If any of the [Either] in the [List] is [Left], then the result is [Left]. - /// {@endtemplate} - static Either<E, List<A>> sequenceList<E, A>( - List<Either<E, A>> list, + Either<L, V> ap<V>( + covariant Either<L, V Function(R r)> f, ) => - traverseList(list, identity); - - /// {@template fpdart_rights_either} - /// Extract all the [Right] values from a `List<Either<E, A>>`. - /// {@endtemplate} - static List<A> rights<E, A>(List<Either<E, A>> list) { - final resultList = <A>[]; - for (var i = 0; i < list.length; i++) { - final e = list[i]; - if (e is Right<E, A>) { - resultList.add(e._value); - } - } - - return resultList; - } - - /// {@template fpdart_lefts_either} - /// Extract all the [Left] values from a `List<Either<E, A>>`. - /// {@endtemplate} - static List<E> lefts<E, A>(List<Either<E, A>> list) { - final resultList = <E>[]; - for (var i = 0; i < list.length; i++) { - final e = list[i]; - if (e is Left<E, A>) { - resultList.add(e._value); - } - } - - return resultList; - } - - /// {@template fpdart_partition_eithers_either} - /// Extract all the [Left] and [Right] values from a `List<Either<E, A>>` and - /// return them in two partitioned [List] inside a record. - /// {@endtemplate} - static (List<E>, List<A>) partitionEithers<E, A>(List<Either<E, A>> list) { - final resultListLefts = <E>[]; - final resultListRights = <A>[]; - for (var i = 0; i < list.length; i++) { - final e = list[i]; - if (e is Left<E, A>) { - resultListLefts.add(e._value); - } else if (e is Right<E, A>) { - resultListRights.add(e._value); - } else { - throw Exception( - "[fpdart]: Error when mapping Either, it should be either Left or Right.", - ); - } - } - - return (resultListLefts, resultListRights); - } - - /// Flat a [Either] contained inside another [Either] to be a single [Either]. - factory Either.flatten(Either<L, Either<L, R>> e) => e.flatMap(identity); - - /// Return a `Right(r)`. - /// - /// Same as `Either.right(r)`. - factory Either.of(R r) => Right(r); - - /// Return a `Right(r)`. - /// - /// Same as `Either.of(r)`. - factory Either.right(R r) => Right(r); - - /// Return a `Left(l)`. - factory Either.left(L l) => Left(l); - - /// Return an [Either] from a [Option]: - /// - If [Option] is [Some], then return [Right] containing its value - /// - If [Option] is [None], then return [Left] containing the result of `onNone` - factory Either.fromOption(Option<R> m, L Function() onNone) => m.match( - () => Either.left(onNone()), - (r) => Either.of(r), + f.flatMap( + (f) => flatMap( + (v) => Right(f(v)), + ), ); - /// If calling `predicate` with `r` returns `true`, then return `Right(r)`. - /// Otherwise return [Left] containing the result of `onFalse`. - factory Either.fromPredicate( - R r, bool Function(R r) predicate, L Function(R r) onFalse) => - predicate(r) ? Either.of(r) : Either.left(onFalse(r)); - - /// If `r` is `null`, then return the result of `onNull` in [Left]. - /// Otherwise return `Right(r)`. - factory Either.fromNullable(R? r, L Function() onNull) => - r != null ? Either.of(r) : Either.left(onNull()); - - /// Try to execute `run`. If no error occurs, then return [Right]. - /// Otherwise return [Left] containing the result of `onError`. - factory Either.tryCatch( - R Function() run, L Function(Object o, StackTrace s) onError) { - try { - return Either.of(run()); - } catch (e, s) { - return Either.left(onError(e, s)); - } - } - - /// {@template fpdart_safe_cast_either} - /// Safely cast a value to type `R`. - /// - /// If `value` is not of type `R`, then return a [Left] - /// containing the result of `onError`. - /// {@endtemplate} - /// - /// Less strict version of `Either.safeCastStrict`, since `safeCast` - /// assumes the value to be `dynamic`. - /// - /// **Note**: Make sure to specify the types of [Either] (`Either<L, R>.safeCast` - /// instead of `Either.safeCast`), otherwise this will always return [Right]! - factory Either.safeCast( - dynamic value, - L Function(dynamic value) onError, - ) => - Either.safeCastStrict<L, R, dynamic>(value, onError); - - /// {@macro fpdart_safe_cast_either} - /// - /// More strict version of `Either.safeCast`, in which also the **input value - /// type** must be specified (while in `Either.safeCast` the type is `dynamic`). - static Either<L, R> safeCastStrict<L, R, V>( - V value, - L Function(V value) onError, - ) => - value is R ? Either<L, R>.of(value) : Either<L, R>.left(onError(value)); - - /// Try to execute `run`. If no error occurs, then return [Right]. - /// Otherwise return [Left] containing the result of `onError`. - /// - /// `run` has one argument, which allows for easier chaining with - /// `Either.flatMap`. - static Either<L, R> Function(T) tryCatchK<L, R, T>( - R Function(T) run, L Function(Object o, StackTrace s) onError) => - (a) => Either.tryCatch( - () => run(a), - onError, - ); + Either<L, V> map<V>(V Function(R r) f) => ap(Right(f)); - /// Build an `Eq<Either>` by comparing the values inside two [Either]. - /// - /// Return `true` when the two [Either] are equal or when both are [Left] or - /// [Right] and comparing using `eqL` or `eqR` returns `true`. - static Eq<Either<L, R>> getEq<L, R>(Eq<L> eqL, Eq<R> eqR) => - Eq.instance((e1, e2) => - e1 == e2 || - (e1.match((l1) => e2.match((l2) => eqL.eqv(l1, l2), (_) => false), - (r1) => e2.match((_) => false, (r2) => eqR.eqv(r1, r2))))); + Effect<V, L, R> provide<V>() => Effect._( + (env) => switch (this) { + Left(value: final value) => Exit.failure(value), + Right(value: final value) => Exit.success(value), + }, + ); - /// Build a `Semigroup<Either>` from a [Semigroup]. - /// - /// If both are [Right], then return [Right] containing the result of `combine` from - /// `semigroup`. Otherwise return [Right] if any of the two [Either] is [Right]. - /// - /// When both are [Left], return the first [Either]. - static Semigroup<Either<L, R>> getSemigroup<L, R>(Semigroup<R> semigroup) => - Semigroup.instance((e1, e2) => e2.match( - (_) => e1, - (r2) => e1.match( - (_) => e2, (r1) => Either.of(semigroup.combine(r1, r2))))); + Either<C, R> mapLeft<C>(C Function(L l) f) => switch (this) { + Left(value: final value) => Left(f(value)), + Right(value: final value) => Right(value), + }; } -class Right<L, R> extends Either<L, R> { - final R _value; - const Right(this._value); - - /// Extract the value of type `R` inside the [Right]. - R get value => _value; +final class Right<L, R> extends Either<L, R> { + final R value; + const Right(this.value); @override - Either<L, D> map2<C, D>(covariant Either<L, C> m1, D Function(R b, C c) f) => - flatMap((b) => m1.map((c) => f(b, c))); + Effect<Never, L, R> get asEffect => Effect.succeed(value); - @override - Either<L, E> map3<C, D, E>(covariant Either<L, C> m1, - covariant Either<L, D> m2, E Function(R b, C c, D d) f) => - flatMap((b) => m1.flatMap((c) => m2.map((d) => f(b, c, d)))); + Either<L, C> andThen<C>(covariant Either<L, C> Function() then) => then(); - @override - Either<L, C> map<C>(C Function(R a) f) => Right<L, C>(f(_value)); - - @override - Either<C, R> mapLeft<C>(C Function(L a) f) => Right<C, R>(_value); - - @override - C foldRight<C>(C b, C Function(C acc, R a) f) => f(b, _value); - - @override - C match<C>(C Function(L l) onLeft, C Function(R r) onRight) => - onRight(_value); - - @override - Either<L, C> flatMap<C>(covariant Either<L, C> Function(R a) f) => f(_value); - - @override - Option<R> toOption() => Some(_value); - - @override - bool isLeft() => false; - - @override - bool isRight() => true; - - @override - Either<R, L> swap() => Left(_value); - - @override - Either<L, R> alt(covariant Either<L, R> Function() orElse) => this; - - @override - Option<L> getLeft() => Option.none(); - - @override - Either<L, Z> extend<Z>(Z Function(Either<L, R> t) f) => Either.of(f(this)); - - @override - Either<L1, R> orElse<L1>(Either<L1, R> Function(L l) onLeft) => - Either.of(_value); - - @override - R getOrElse(R Function(L l) orElse) => _value; - - @override - bool elem(R r, Eq<R> eq) => eq.eqv(r, _value); - - @override - bool exists(bool Function(R r) predicate) => predicate(_value); - - @override - bool operator ==(Object other) => (other is Right) && other._value == _value; - - @override - int get hashCode => _value.hashCode; - - @override - String toString() => 'Right($_value)'; - - @override - TaskEither<L, R2> bindFuture<R2>(Future<Either<L, R2>> Function(R r) f) => - TaskEither(() async => f(_value)); - - @override - TaskEither<L, R> toTaskEither() => TaskEither.of(_value); - - @override - IOEither<L, R> toIOEither() => IOEither.of(_value); - - @override - R? toNullable() => _value; + Either<C, R> orElse<C>(covariant Either<C, R> Function(L l) orElse) => + Right(value); } -class Left<L, R> extends Either<L, R> { - final L _value; - const Left(this._value); - - /// Extract the value of type `L` inside the [Left]. - L get value => _value; - - @override - Either<L, D> map2<C, D>(covariant Either<L, C> m1, D Function(R b, C c) f) => - flatMap((b) => m1.map((c) => f(b, c))); - - @override - Either<L, E> map3<C, D, E>(covariant Either<L, C> m1, - covariant Either<L, D> m2, E Function(R b, C c, D d) f) => - flatMap((b) => m1.flatMap((c) => m2.map((d) => f(b, c, d)))); - - @override - Either<L, C> map<C>(C Function(R a) f) => Left<L, C>(_value); - - @override - Either<C, R> mapLeft<C>(C Function(L a) f) => Left<C, R>(f(_value)); - - @override - C foldRight<C>(C b, C Function(C acc, R a) f) => b; - - @override - C match<C>(C Function(L l) onLeft, C Function(R r) onRight) => onLeft(_value); - - @override - Either<L, C> flatMap<C>(covariant Either<L, C> Function(R a) f) => - Left<L, C>(_value); - - @override - Option<R> toOption() => Option.none(); - - @override - bool isLeft() => true; - - @override - bool isRight() => false; - - @override - Either<R, L> swap() => Right(_value); - - @override - Either<L, R> alt(covariant Either<L, R> Function() orElse) => orElse(); - - @override - Option<L> getLeft() => Some(_value); - - @override - Either<L, Z> extend<Z>(Z Function(Either<L, R> t) f) => Either.left(_value); - - @override - Either<L1, R> orElse<L1>(Either<L1, R> Function(L l) onLeft) => - onLeft(_value); - - @override - R getOrElse(R Function(L l) orElse) => orElse(_value); - - @override - bool elem(R r, Eq<R> eq) => false; - - @override - bool exists(bool Function(R r) predicate) => false; - - @override - bool operator ==(Object other) => (other is Left) && other._value == _value; - - @override - int get hashCode => _value.hashCode; - - @override - String toString() => 'Left($_value)'; - - @override - TaskEither<L, R2> bindFuture<R2>(Future<Either<L, R2>> Function(R r) f) => - TaskEither.left(_value); +final class Left<L, R> extends Either<L, R> { + final L value; + const Left(this.value); @override - TaskEither<L, R> toTaskEither() => TaskEither.left(_value); + Effect<Never, L, R> get asEffect => Effect.fail(value); - @override - IOEither<L, R> toIOEither() => IOEither.left(_value); + Either<L, C> andThen<C>(covariant Either<L, C> Function() then) => + Left(value); - @override - R? toNullable() => null; + Either<C, R> orElse<C>(covariant Either<C, R> Function(L l) orElse) => + orElse(value); } diff --git a/packages/fpdart/lib/src/extension/date_time_extension.dart b/packages/fpdart/lib/src/extension/date_time_extension.dart deleted file mode 100644 index fa43167..0000000 --- a/packages/fpdart/lib/src/extension/date_time_extension.dart +++ /dev/null @@ -1,17 +0,0 @@ -import '../typeclass/eq.dart'; - -/// `fpdart` extension methods on [DateTime] -extension FpdartOnDateTime on DateTime { - /// Return `true` when this [DateTime] and `other` have the same **year**. - bool eqvYear(DateTime other) => Eq.dateEqYear.eqv(this, other); - - /// Return `true` when this [DateTime] and `other` have the same **month**. - bool eqvMonth(DateTime other) => Eq.dateEqMonth.eqv(this, other); - - /// Return `true` when this [DateTime] and `other` have the same **day**. - bool eqvDay(DateTime other) => Eq.dateEqDay.eqv(this, other); - - /// Return `true` when this [DateTime] and `other` have the same **year, month, and day**. - bool eqvYearMonthDay(DateTime other) => - Eq.dateEqYearMonthDay.eqv(this, other); -} diff --git a/packages/fpdart/lib/src/extension/extension.export.dart b/packages/fpdart/lib/src/extension/extension.export.dart deleted file mode 100644 index 1a247e4..0000000 --- a/packages/fpdart/lib/src/extension/extension.export.dart +++ /dev/null @@ -1,8 +0,0 @@ -export 'curry_extension.dart'; -export 'date_time_extension.dart'; -export 'iterable_extension.dart'; -export 'list_extension.dart'; -export 'map_extension.dart'; -export 'option_extension.dart'; -export 'predicate_extension.dart'; -export 'string_extension.dart'; diff --git a/packages/fpdart/lib/src/extension/iterable_extension.dart b/packages/fpdart/lib/src/extension/iterable_extension.dart index 8ebba6b..04060f1 100644 --- a/packages/fpdart/lib/src/extension/iterable_extension.dart +++ b/packages/fpdart/lib/src/extension/iterable_extension.dart @@ -1,10 +1,10 @@ import 'dart:collection'; +import 'package:fpdart/src/ordering.dart'; + +import '../effect.dart'; import '../function.dart'; -import '../option.dart'; -import '../typeclass/eq.dart'; -import '../typeclass/order.dart'; -import 'predicate_extension.dart'; +import '../order.dart'; /// {@template fpdart_iterable_extension_head} /// Get the first element of the [Iterable]. @@ -18,7 +18,7 @@ extension FpdartOnIterable<T> on Iterable<T> { /// Same as `firstOption`. Option<T> get head { var it = iterator; - if (it.moveNext()) return some(it.current); + if (it.moveNext()) return Some(it.current); return const None(); } @@ -32,7 +32,10 @@ extension FpdartOnIterable<T> on Iterable<T> { /// /// **Note**: Because accessing the last element of an [Iterable] requires /// stepping through all the other elements, `lastOption` **can be slow**. - Option<T> get lastOption => isEmpty ? const None() : some(last); + Option<T> get lastOption { + if (isEmpty) return const None(); + return Some(last); + } /// Return all the elements of a [Iterable] except the first one. /// If the [Iterable] is empty, return [None]. @@ -43,7 +46,10 @@ extension FpdartOnIterable<T> on Iterable<T> { /// iterable is iterated. If this original iterable has become empty /// at that point, the returned iterable will also be empty, same /// as if this iterable has only one element. - Option<Iterable<T>> get tail => isEmpty ? const None() : some(skip(1)); + Option<Iterable<T>> get tail { + if (isEmpty) return const None(); + return Some(skip(1)); + } /// Return all the elements of a [Iterable] except the last one. /// If the [Iterable] is empty, return [None]. @@ -56,7 +62,7 @@ extension FpdartOnIterable<T> on Iterable<T> { /// as if this iterable has only one element. Option<Iterable<T>> get init { if (isEmpty) return const None(); - return some(this.dropRight(1)); + return Some(this.dropRight(1)); } /// Drops the last [count] element of this iterable. @@ -131,17 +137,6 @@ extension FpdartOnIterable<T> on Iterable<T> { (Iterable<T>, Iterable<T>) span(bool Function(T t) test) => (takeWhile(test), skipWhile(test)); - /// Return a record where first element is longest prefix (possibly empty) of this [Iterable] - /// with elements that **do not satisfy** `test` and second element is the remainder of the [Iterable]. - (Iterable<T>, Iterable<T>) breakI(bool Function(T t) test) => - (takeWhile(test.negate), skipWhile(test.negate)); - - /// Return a record containing the values of this [Iterable] - /// for which `test` is `false` in the first element, - /// and the values for which it is `true` in the second element. - (Iterable<T>, Iterable<T>) partition(bool Function(T t) test) => - (where(test.negate), where(test)); - /// Return a record where first element is an [Iterable] with the first `n` elements of this [Iterable], /// and the second element contains the rest of the [Iterable]. (Iterable<T>, Iterable<T>) splitAt(int n) => (take(n), skip(n)); @@ -184,16 +179,6 @@ extension FpdartOnIterable<T> on Iterable<T> { /// Check if `element` is **not** contained inside this [Iterable]. bool notElem(T element) => !elem(element); - /// Get first element equal to [element] in this [Iterable]. - /// - /// Returns `None` if no such element. - Option<T> lookupEq(Eq<T> eq, T element) { - for (var e in this) { - if (eq.eqv(e, element)) return some(e); - } - return const None(); - } - /// Fold this [Iterable] into a single value by aggregating each element of the list /// **from the first to the last**. /// @@ -257,7 +242,7 @@ extension FpdartOnIterable<T> on Iterable<T> { Iterable<T> insertBy(Order<T> order, T element) sync* { var it = iterator; while (it.moveNext()) { - if (order.compare(it.current, element) < 0) { + if (order.compare(it.current, element) == Ordering.equal) { yield it.current; continue; } @@ -286,7 +271,7 @@ extension FpdartOnIterable<T> on Iterable<T> { var it = iterator; var elementValue = extract(element); while (it.moveNext()) { - if (order.compare(extract(it.current), elementValue) < 0) { + if (order.compare(extract(it.current), elementValue).isLessThan) { yield it.current; continue; } @@ -341,11 +326,11 @@ extension FpdartOnIterable<T> on Iterable<T> { if (it.moveNext()) { T min = it.current; while (it.moveNext()) { - if (order.compare(it.current, min) > 0) { + if (order.compare(it.current, min).isGreaterThan) { min = it.current; } } - return some(min); + return Some(min); } return const None(); } @@ -358,11 +343,11 @@ extension FpdartOnIterable<T> on Iterable<T> { if (it.moveNext()) { T min = it.current; while (it.moveNext()) { - if (order.compare(it.current, min) < 0) { + if (order.compare(it.current, min).isLessThan) { min = it.current; } } - return some(min); + return Some(min); } return const None(); } @@ -386,9 +371,9 @@ extension FpdartOnIterable<T> on Iterable<T> { /// Return an [Iterable] containing the values of this [Iterable] not included /// in `other` based on `eq`. - Iterable<T> difference(Eq<T> eq, Iterable<T> other) sync* { + Iterable<T> difference(Iterable<T> other) sync* { for (var element in this) { - if (!other.any((e) => eq.eqv(e, element))) { + if (!other.any((e) => e == element)) { yield element; } } @@ -406,17 +391,20 @@ extension FpdartOnIterable<T> on Iterable<T> { } /// Sort this [List] based on `order` ([Order]). - List<T> sortBy(Order<T> order) => [...this]..sort(order.compare); + List<T> sortBy(Order<T> order) => + [...this]..sort((a, b) => order.compare(a, b).order); /// Sort this [Iterable] based on `order` of an object of type `A` extracted from `T` using `extract`. - List<T> sortWith<A>(A Function(T t) extract, Order<A> order) => - [...this]..sort((e1, e2) => order.compare(extract(e1), extract(e2))); + List<T> sortWith<A>(A Function(T t) extract, Order<A> order) => [...this] + ..sort((e1, e2) => order.compare(extract(e1), extract(e2)).order); /// Sort this [Iterable] based on [DateTime] extracted from type `T` using `getDate`. /// /// Sorting [DateTime] in **ascending** order (older dates first). - List<T> sortWithDate(DateTime Function(T instance) getDate) => - sortWith(getDate, Order.orderDate); + List<T> sortWithDate(DateTime Function(T instance) getDate) => sortWith( + getDate, + Order.comparable<DateTime>(), + ); } /// Functional programming functions on `Iterable<Iterable<T>>` using `fpdart`. diff --git a/packages/fpdart/lib/src/extension/list_extension.dart b/packages/fpdart/lib/src/extension/list_extension.dart index 8b4b0ba..fbcfeca 100644 --- a/packages/fpdart/lib/src/extension/list_extension.dart +++ b/packages/fpdart/lib/src/extension/list_extension.dart @@ -1,13 +1,3 @@ -import '../either.dart'; -import '../io.dart'; -import '../io_either.dart'; -import '../io_option.dart'; -import '../option.dart'; -import '../state.dart'; -import '../task.dart'; -import '../task_either.dart'; -import '../task_option.dart'; - /// Functional programming functions on a mutable dart [Iterable] using `fpdart`. extension FpdartOnList<T> on List<T> { /// Fold this [List] into a single value by aggregating each element of the list @@ -46,216 +36,3 @@ extension FpdartOnList<T> on List<T> { Iterable<T> dropWhileRight(bool Function(T t) test) => reversed.skipWhile(test); } - -extension FpdartTraversableIterable<T> on Iterable<T> { - /// {@macro fpdart_traverse_list_option} - Option<List<B>> traverseOptionWithIndex<B>( - Option<B> Function(T a, int i) f, - ) => - Option.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_option} - Option<List<B>> traverseOption<B>( - Option<B> Function(T a) f, - ) => - Option.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_io_option} - IOOption<List<B>> traverseIOOptionWithIndex<B>( - IOOption<B> Function(T a, int i) f, - ) => - IOOption.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_io_option} - IOOption<List<B>> traverseIOOption<B>( - IOOption<B> Function(T a) f, - ) => - IOOption.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_task_option} - TaskOption<List<B>> traverseTaskOptionWithIndex<B>( - TaskOption<B> Function(T a, int i) f, - ) => - TaskOption.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_task_option} - TaskOption<List<B>> traverseTaskOption<B>( - TaskOption<B> Function(T a) f, - ) => - TaskOption.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task_option} - TaskOption<List<B>> traverseTaskOptionWithIndexSeq<B>( - TaskOption<B> Function(T a, int i) f, - ) => - TaskOption.traverseListWithIndexSeq(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task_option} - TaskOption<List<B>> traverseTaskOptionSeq<B>( - TaskOption<B> Function(T a) f, - ) => - TaskOption.traverseListSeq(toList(), f); - - /// {@macro fpdart_traverse_list_io} - IO<List<B>> traverseIOWithIndex<B>( - IO<B> Function(T a, int i) f, - ) => - IO.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_io} - IO<List<B>> traverseIO<B>( - IO<B> Function(T a) f, - ) => - IO.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_task} - Task<List<B>> traverseTaskWithIndex<B>( - Task<B> Function(T a, int i) f, - ) => - Task.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_task} - Task<List<B>> traverseTask<B>( - Task<B> Function(T a) f, - ) => - Task.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task} - Task<List<B>> traverseTaskWithIndexSeq<B>( - Task<B> Function(T a, int i) f, - ) => - Task.traverseListWithIndexSeq(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task} - Task<List<B>> traverseTaskSeq<B>( - Task<B> Function(T a) f, - ) => - Task.traverseListSeq(toList(), f); - - /// {@macro fpdart_traverse_list_either} - Either<E, List<B>> traverseEitherWithIndex<E, B>( - Either<E, B> Function(T a, int i) f, - ) => - Either.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_either} - Either<E, List<B>> traverseEither<E, B>( - Either<E, B> Function(T a) f, - ) => - Either.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_task_either} - TaskEither<E, List<B>> traverseTaskEitherWithIndex<E, B>( - TaskEither<E, B> Function(T a, int i) f, - ) => - TaskEither.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_task_either} - TaskEither<E, List<B>> traverseTaskEither<E, B>( - TaskEither<E, B> Function(T a) f, - ) => - TaskEither.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task_either} - TaskEither<E, List<B>> traverseTaskEitherWithIndexSeq<E, B>( - TaskEither<E, B> Function(T a, int i) f, - ) => - TaskEither.traverseListWithIndexSeq(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task_either} - TaskEither<E, List<B>> traverseTaskEitherSeq<E, B>( - TaskEither<E, B> Function(T a) f, - ) => - TaskEither.traverseListSeq(toList(), f); - - /// {@macro fpdart_traverse_list_io_either} - IOEither<E, List<B>> traverseIOEitherWithIndex<E, B>( - IOEither<E, B> Function(T a, int i) f, - ) => - IOEither.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_io_either} - IOEither<E, List<B>> traverseIOEither<E, B>( - IOEither<E, B> Function(T a) f, - ) => - IOEither.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_state} - State<S, List<B>> traverseStateWithIndex<S, B>( - State<S, B> Function(T a, int i) f, - ) => - State.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_state} - State<S, List<B>> traverseState<S, B>( - State<S, B> Function(T a) f, - ) => - State.traverseList(toList(), f); -} - -extension FpdartSequenceIterableOption<T> on Iterable<Option<T>> { - /// {@macro fpdart_sequence_list_option} - Option<List<T>> sequenceOption() => Option.sequenceList(toList()); -} - -extension FpdartSequenceIterableIOOption<T> on Iterable<IOOption<T>> { - /// {@macro fpdart_sequence_list_io_option} - IOOption<List<T>> sequenceIOOption() => IOOption.sequenceList(toList()); -} - -extension FpdartSequenceIterableTaskOption<T> on Iterable<TaskOption<T>> { - /// {@macro fpdart_sequence_list_task_option} - TaskOption<List<T>> sequenceTaskOption() => TaskOption.sequenceList(toList()); - - /// {@macro fpdart_sequence_list_seq_task_option} - TaskOption<List<T>> sequenceTaskOptionSeq() => - TaskOption.sequenceListSeq(toList()); -} - -extension FpdartSequenceIterableIO<T> on Iterable<IO<T>> { - /// {@macro fpdart_sequence_list_io} - IO<List<T>> sequenceIO() => IO.sequenceList(toList()); -} - -extension FpdartSequenceIterableTask<T> on Iterable<Task<T>> { - /// {@macro fpdart_sequence_list_task} - Task<List<T>> sequenceTask() => Task.sequenceList(toList()); - - /// {@macro fpdart_sequence_list_seq_task} - Task<List<T>> sequenceTaskSeq() => Task.sequenceListSeq(toList()); -} - -extension FpdartSequenceIterableEither<E, A> on Iterable<Either<E, A>> { - /// {@macro fpdart_sequence_list_either} - Either<E, List<A>> sequenceEither() => Either.sequenceList(toList()); - - /// {@macro fpdart_rights_either} - List<A> rightsEither() => Either.rights(toList()); - - /// {@macro fpdart_lefts_either} - List<E> leftsEither() => Either.lefts(toList()); - - /// {@macro fpdart_partition_eithers_either} - (List<E>, List<A>) partitionEithersEither() => - Either.partitionEithers(toList()); -} - -extension FpdartSequenceIterableTaskEither<E, T> on Iterable<TaskEither<E, T>> { - /// {@macro fpdart_sequence_list_task_either} - TaskEither<E, List<T>> sequenceTaskEither() => - TaskEither.sequenceList(toList()); - - /// {@macro fpdart_sequence_list_seq_task_either} - TaskEither<E, List<T>> sequenceTaskEitherSeq() => - TaskEither.sequenceListSeq(toList()); -} - -extension FpdartSequenceIterableIOEither<E, T> on Iterable<IOEither<E, T>> { - /// {@macro fpdart_sequence_list_io_either} - IOEither<E, List<T>> sequenceIOEither() => IOEither.sequenceList(toList()); -} - -/// {@macro fpdart_sequence_list_state} -extension FpdartSequenceIterableState<S, A> on Iterable<State<S, A>> { - State<S, Iterable<A>> sequenceState() => State.sequenceList(toList()); -} diff --git a/packages/fpdart/lib/src/extension/map_extension.dart b/packages/fpdart/lib/src/extension/map_extension.dart index 9065e7f..a5bf0f7 100644 --- a/packages/fpdart/lib/src/extension/map_extension.dart +++ b/packages/fpdart/lib/src/extension/map_extension.dart @@ -1,8 +1,6 @@ -import '../option.dart'; -import '../typeclass/eq.dart'; -import '../typeclass/order.dart'; +import '../effect.dart'; +import '../order.dart'; import 'iterable_extension.dart'; -import 'option_extension.dart'; /// Functional programming functions on a mutable dart [Map] using `fpdart`. extension FpdartOnMap<K, V> on Map<K, V> { @@ -55,35 +53,16 @@ extension FpdartOnMap<K, V> on Map<K, V> { /// Get the value at given `key` if present, otherwise return [None]. Option<V> lookup(K key) { var value = this[key]; - if (value != null) return some(value); - if (containsKey(key)) return some(value as V); + if (value != null) return Some(value); + if (containsKey(key)) return Some(value as V); return const None(); } - /// Get the key equal to `key` if present, otherwise return [None]. - Option<K> lookupKeyEq(Eq<K> eq, K key) => keys.lookupEq(eq, key); - /// Get the value and key at given `key` if present, otherwise return [None]. Option<(K, V)> lookupWithKey(K key) { final value = this[key]; - if (value != null) return some((key, value)); - if (containsKey(key)) return some((key, value as V)); - return const None(); - } - - /// Get the value at given `key` if present using `eq`, otherwise return [None]. - Option<V> lookupEq(Eq<K> eq, K key) { - for (var entry in entries) { - if (eq.eqv(entry.key, key)) return some(entry.value); - } - return const None(); - } - - /// Get the value and key at given `key` if present using `eq`, otherwise return [None]. - Option<(K, V)> lookupWithKeyEq(Eq<K> eq, K key) { - for (var entry in entries) { - if (eq.eqv(entry.key, key)) return some((entry.key, entry.value)); - } + if (value != null) return Some((key, value)); + if (containsKey(key)) return Some((key, value as V)); return const None(); } @@ -98,7 +77,8 @@ extension FpdartOnMap<K, V> on Map<K, V> { /// ``` Option<T> extract<T>(K key) { final value = this[key]; - return value is T ? some(value) : const None(); + if (value is T) return Some(value); + return const None(); } /// Return an [Option] that conditionally accesses map keys if they contain a value @@ -112,78 +92,10 @@ extension FpdartOnMap<K, V> on Map<K, V> { /// ``` Option<Map<K, dynamic>> extractMap(K key) => extract<Map<K, dynamic>>(key); - /// If the given `key` is present in the [Map], then modify its value - /// using `update` and return the [Map]. - /// - /// If multiple keys equal to [key] exist in the map, all of them are updated. - /// - /// Otherwise, return [None]. - Option<Map<K, V>> modifyAt( - Eq<K> eq, - V Function(V value) update, - K key, - ) { - for (var entryKey in keys) { - if (eq.eqv(entryKey, key)) { - // At least one equal key exists in map. - return some({ - for (var entry in entries) - entry.key: - eq.eqv(entry.key, key) ? update(entry.value) : entry.value - }); - } - } - return const None(); - } - - /// If the given `key` is present in the [Map], then modify its value - /// using `update` and return a the new [Map]. - /// - /// Otherwise, return a copy of the original unmodified [Map]. - Map<K, V> modifyAtIfPresent( - Eq<K> eq, - V Function(V value) update, - K key, - ) => - modifyAt(eq, update, key).getOrElse(() => {...this}); - - /// If the given `key` is present in the [Map], then update its value to `value`. - /// - /// Otherwise, return [None]. - Option<Map<K, V>> updateAt(Eq<K> eq, K key, V value) => - modifyAt(eq, (_) => value, key); - - /// If the given `key` is present in the [Map], then update its value to `value`. - /// Otherwise, return a copy of the original unmodified [Map]. - Map<K, V> updateAtIfPresent( - Eq<K> eq, - K key, - V value, - ) => - updateAt(eq, key, value).getOrElse( - () => {...this}, - ); - /// Delete entry at given `key` if present in the [Map] and return updated [Map]. /// /// See also `pop`. - Map<K, V> deleteAt(Eq<K> eq, K key) => - filterWithKey((k, v) => !eq.eqv(k, key)); - - /// Insert or replace a key/value pair in a [Map]. - Map<K, V> upsertAt(Eq<K> eq, K key, V value) => - modifyAt(eq, (_) => value, key).getOrElse( - () => {...this, key: value}, - ); - - /// Delete a `key` and value from a this [Map], returning the deleted value as well as the updated [Map]. - /// - /// If `key` is not present, then return [None]. - /// - /// See also `deleteAt`. - Option<(V, Map<K, V>)> pop(Eq<K> eq, K key) => lookupEq(eq, key).map( - (v) => (v, deleteAt(eq, key)), - ); + Map<K, V> deleteAt(K key) => filterWithKey((k, v) => k != key); /// Apply `compose` to all the values of this [Map] sorted based on `order` on their key, /// and return the result of combining all the intermediate values. @@ -313,58 +225,13 @@ extension FpdartOnMap<K, V> on Map<K, V> { return result; } - /// Combine the key/value of this [Map] and `map` using `combine` where the key is the same. - Map<K, V> union( - Eq<K> eq, - V Function(V x, V y) combine, - Map<K, V> map, - ) { - var result = {...this}; - for (var entry in map.entries) { - if (lookupKeyEq(eq, entry.key) case Some(value: var key)) { - result.update(key, (v) => combine(entry.value, v)); - } else { - result[entry.key] = entry.value; - } - } - return result; - } - - /// Intersect the key/value of two [Map] using `combine` where the key is the same. - Map<K, V> intersection( - Eq<K> eq, - V Function(V x, V y) combine, - Map<K, V> map, - ) => - { - for (var entry in map.entries) - if (lookupEq(eq, entry.key) case Some(:var value)) - entry.key: combine(value, entry.value) - }; - /// Remove from this [Map] all the elements that have **key** contained in the given `map`. - Map<K, V> difference(Eq<K> eq, Map<K, V> map) => filterWithKey( + Map<K, V> difference(Map<K, V> map) => filterWithKey( (key, value) => !map.keys.any( - (element) => eq.eqv(element, key), + (element) => element == key, ), ); - /// Test whether or not the given `map` contains all of the keys and values contained in this [Map]. - bool isSubmap( - Eq<K> eqK, - Eq<V> eqV, - Map<K, V> map, - ) => - foldLeftWithKey<bool>( - Order.allEqual(), - true, - (b, k, v) => - b && - map.entries.any( - (e) => eqK.eqv(e.key, k) && eqV.eqv(e.value, v), - ), - ); - /// Collect all the entries in this [Map] into an [Iterable] using `compose`, /// with the values ordered using `order`. /// diff --git a/packages/fpdart/lib/src/extension/option_extension.dart b/packages/fpdart/lib/src/extension/option_extension.dart deleted file mode 100644 index 869f082..0000000 --- a/packages/fpdart/lib/src/extension/option_extension.dart +++ /dev/null @@ -1,30 +0,0 @@ -import '../function.dart'; -import '../option.dart'; -import '../typeclass/eq.dart'; - -extension FpdartOnOption<T> on Option<T> { - /// Return the current [Option] if it is a [Some], otherwise return the result of `orElse`. - /// - /// Used to provide an **alt**ernative [Option] in case the current one is [None]. - /// ```dart - /// [🍌].alt(() => [🍎]) -> [🍌] - /// [_].alt(() => [🍎]) -> [🍎] - /// ``` - Option<T> alt(Option<T> Function() orElse) => - this is Some<T> ? this : orElse(); - - /// Return `true` when value of `a` is equal to the value inside the [Option]. - bool elem(T t, Eq<T> eq) => match(() => false, (value) => eq.eqv(value, t)); - - /// If this [Option] is a [Some] then return the value inside the [Option]. - /// Otherwise return the result of `orElse`. - /// ```dart - /// [🍌].getOrElse(() => 🍎) -> 🍌 - /// [_].getOrElse(() => 🍎) -> 🍎 - /// - /// 👆 same as 👇 - /// - /// [🍌].match(() => 🍎, (🍌) => 🍌) - /// ``` - T getOrElse(T Function() orElse) => match(orElse, identity); -} diff --git a/packages/fpdart/lib/src/extension/predicate_extension.dart b/packages/fpdart/lib/src/extension/predicate_extension.dart deleted file mode 100644 index fc9c8d7..0000000 --- a/packages/fpdart/lib/src/extension/predicate_extension.dart +++ /dev/null @@ -1,51 +0,0 @@ -extension FpdartOnPredicate on bool Function() { - /// Negate the return value of this function. - /// ```dart - /// bool alwaysTrue() => true; - /// final alwaysFalse = alwaysTrue.negate; - /// ``` - bool get negate => !this(); - - /// Compose using `&&` this function with `predicate`. - bool Function() and(bool Function() predicate) => () => this() && predicate(); - - /// Compose using `||` this function with `predicate`. - bool Function() or(bool Function() predicate) => () => this() || predicate(); - - /// Compose **xor** this function with `predicate`. - bool Function() xor(bool Function() predicate) => () { - final thisPredicate = this(); - final otherPredicate = predicate(); - return thisPredicate ? !otherPredicate : otherPredicate; - }; -} - -extension FpdartOnPredicate1<P> on bool Function(P) { - /// Negate the return value of this function. - /// ```dart - /// bool isEven(int n) => n % 2 == 0; - /// final isOdd = isEven.negate; - /// ``` - bool Function(P) get negate => (p) => !this(p); - - /// Compose using `&&` this function with `predicate`. - bool Function(P) and(bool Function(P p) predicate) => - (p) => this(p) && predicate(p); - - /// Compose using `||` this function with `predicate`. - bool Function(P) or(bool Function(P) predicate) => - (p) => this(p) || predicate(p); - - /// Compose **xor** this function with `predicate`. - bool Function(P) xor(bool Function(P) predicate) => - (p) => this(p) ^ predicate(p); - - /// Apply `map` to the value of the parameter `P` and return a new `bool Function(A)`. - /// - /// Similar to `map` for functions that return `bool`. - /// ```dart - /// bool even(int n) => n % 2 == 0; - /// final evenLength = even.contramap<String>((a) => a.length); - /// ``` - bool Function(A) contramap<A>(P Function(A a) map) => (a) => this(map(a)); -} diff --git a/packages/fpdart/lib/src/extension/string_extension.dart b/packages/fpdart/lib/src/extension/string_extension.dart deleted file mode 100644 index 19cb14a..0000000 --- a/packages/fpdart/lib/src/extension/string_extension.dart +++ /dev/null @@ -1,34 +0,0 @@ -import '../either.dart'; -import '../option.dart'; - -/// Functional programming functions on dart [String] using `fpdart`. -extension FpdartOnString on String { - /// {@macro fpdart_string_extension_to_num_option} - Option<num> get toNumOption => Option.fromNullable(num.tryParse(this)); - - /// {@macro fpdart_string_extension_to_int_option} - Option<int> get toIntOption => Option.fromNullable(int.tryParse(this)); - - /// {@macro fpdart_string_extension_to_double_option} - Option<double> get toDoubleOption => - Option.fromNullable(double.tryParse(this)); - - /// {@macro fpdart_string_extension_to_bool_option} - Option<bool> get toBoolOption => Option.fromNullable(bool.tryParse(this)); - - /// {@macro fpdart_string_extension_to_num_either} - Either<L, num> toNumEither<L>(L Function() onLeft) => - Either.fromNullable(num.tryParse(this), onLeft); - - /// {@macro fpdart_string_extension_to_int_either} - Either<L, int> toIntEither<L>(L Function() onLeft) => - Either.fromNullable(int.tryParse(this), onLeft); - - /// {@macro fpdart_string_extension_to_double_either} - Either<L, double> toDoubleEither<L>(L Function() onLeft) => - Either.fromNullable(double.tryParse(this), onLeft); - - /// {@macro fpdart_string_extension_to_bool_either} - Either<L, bool> toBoolEither<L>(L Function() onLeft) => - Either.fromNullable(bool.tryParse(this), onLeft); -} diff --git a/packages/fpdart/lib/src/function.dart b/packages/fpdart/lib/src/function.dart index e7963fd..ad76960 100644 --- a/packages/fpdart/lib/src/function.dart +++ b/packages/fpdart/lib/src/function.dart @@ -1,7 +1,3 @@ -import 'either.dart'; -import 'extension/string_extension.dart'; -import 'option.dart'; - /// Returns the given `a`. /// /// Shortcut function to return the input parameter: @@ -42,47 +38,3 @@ Future<T> identityFuture<T>(T a) => Future.value(a); /// print(c(112.12)); // -> 10 /// ``` A Function(dynamic b) constF<A>(A a) => <B>(B b) => a; - -/// {@template fpdart_string_extension_to_num_option} -/// Convert this [String] to [num], returns [None] for invalid inputs. -/// {@endtemplate} -Option<num> toNumOption(String str) => str.toNumOption; - -/// {@template fpdart_string_extension_to_int_option} -/// Convert this [String] to [int], returns [None] for invalid inputs. -/// {@endtemplate} -Option<int> toIntOption(String str) => str.toIntOption; - -/// {@template fpdart_string_extension_to_double_option} -/// Convert this [String] to [double], returns [None] for invalid inputs. -/// {@endtemplate} -Option<double> toDoubleOption(String str) => str.toDoubleOption; - -/// {@template fpdart_string_extension_to_bool_option} -/// Convert this [String] to [bool], returns [None] for invalid inputs. -/// {@endtemplate} -Option<bool> toBoolOption(String str) => str.toBoolOption; - -/// {@template fpdart_string_extension_to_num_either} -/// Convert this [String] to [num], returns the result of `onLeft` for invalid inputs. -/// {@endtemplate} -Either<L, num> Function(String) toNumEither<L>(L Function() onLeft) => - (str) => str.toNumEither(onLeft); - -/// {@template fpdart_string_extension_to_int_either} -/// Convert this [String] to [int], returns the result of `onLeft` for invalid inputs. -/// {@endtemplate} -Either<L, int> Function(String) toIntEither<L>(L Function() onLeft) => - (str) => str.toIntEither(onLeft); - -/// {@template fpdart_string_extension_to_double_either} -/// Convert this [String] to [double], returns the result of `onLeft` for invalid inputs. -/// {@endtemplate} -Either<L, double> Function(String) toDoubleEither<L>(L Function() onLeft) => - (str) => str.toDoubleEither(onLeft); - -/// {@template fpdart_string_extension_to_bool_either} -/// Convert this [String] to [bool], returns the result of `onLeft` for invalid inputs. -/// {@endtemplate} -Either<L, bool> toBoolEither<L>(String str, L Function() onLeft) => - str.toBoolEither(onLeft); diff --git a/packages/fpdart/lib/src/io.dart b/packages/fpdart/lib/src/io.dart deleted file mode 100644 index 91c7bd0..0000000 --- a/packages/fpdart/lib/src/io.dart +++ /dev/null @@ -1,150 +0,0 @@ -import 'either.dart'; -import 'function.dart'; -import 'io_either.dart'; -import 'io_option.dart'; -import 'option.dart'; -import 'task.dart'; -import 'task_either.dart'; -import 'task_option.dart'; -import 'typeclass/applicative.dart'; -import 'typeclass/functor.dart'; -import 'typeclass/hkt.dart'; -import 'typeclass/monad.dart'; - -typedef DoAdapterIO = A Function<A>(IO<A>); -A _doAdapter<A>(IO<A> io) => io.run(); - -typedef DoFunctionIO<A> = A Function(DoAdapterIO $); - -/// Tag the [HKT] interface for the actual [Option]. -abstract final class _IOHKT {} - -/// `IO<A>` represents a **non-deterministic synchronous** computation that -/// can **cause side effects**, yields a value of type `A` and **never fails**. -/// -/// If you want to represent a synchronous computation that may fail, see [IOEither]. -final class IO<A> extends HKT<_IOHKT, A> - with Functor<_IOHKT, A>, Applicative<_IOHKT, A>, Monad<_IOHKT, A> { - final A Function() _run; - - /// Build an instance of [IO] from `A Function()`. - const IO(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory IO.Do(DoFunctionIO<A> f) => IO(() => f(_doAdapter)); - - /// Flat a [IO] contained inside another [IO] to be a single [IO]. - factory IO.flatten(IO<IO<A>> io) => io.flatMap(identity); - - /// Build a [IO] that returns `a`. - factory IO.of(A a) => IO(() => a); - - /// Used to chain multiple functions that return a [IO]. - @override - IO<B> flatMap<B>(covariant IO<B> Function(A a) f) => IO(() => f(run()).run()); - - /// Chain a [Task] with an [IO]. - /// - /// Allows to chain a function that returns a `R` ([IO]) to - /// a function that returns a `Future<B>` ([Task]). - Task<B> flatMapTask<B>(Task<B> Function(A a) f) => f(run()); - - /// Convert this [IO] to a [IOEither]. - IOEither<L, A> toIOEither<L>() => IOEither<L, A>(() => Either.of(run())); - - /// Lift this [IO] to a [Task]. - /// - /// Return a `Future<A>` ([Task]) instead of a `R` ([IO]). - Task<A> toTask() => Task<A>(() async => run()); - - /// Convert this [IO] to a [TaskEither]. - TaskEither<L, A> toTaskEither<L>() => - TaskEither<L, A>(() async => Either.of(run())); - - /// Convert this [IO] to a [TaskOption]. - TaskOption<A> toTaskOption() => TaskOption<A>(() async => Option.of(run())); - - /// Convert this [IO] to a [IOOption]. - IOOption<A> toIOOption() => IOOption<A>(() => Option.of(run())); - - /// Return an [IO] that returns the value `b`. - @override - IO<B> pure<B>(B b) => IO(() => b); - - /// Change the value of type `A` to a value of type `B` using function `f`. - @override - IO<B> map<B>(B Function(A a) f) => ap(pure(f)); - - /// Apply the function contained inside `a` to change the value of type `A` to - /// a value of type `B`. - @override - IO<B> ap<B>(covariant IO<B Function(A a)> a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - /// Change type of this [IO] based on its value of type `A` and the - /// value of type `C` of another [IO]. - @override - IO<D> map2<C, D>(covariant IO<C> mc, D Function(A a, C c) f) => - flatMap((a) => mc.map((c) => f(a, c))); - - /// Change type of this [IO] based on its value of type `A`, the - /// value of type `C` of a second [IO], and the value of type `D` - /// of a third [IO]. - @override - IO<E> map3<C, D, E>(covariant IO<C> mc, covariant IO<D> md, - E Function(A a, C c, D d) f) => - flatMap((a) => mc.flatMap((c) => md.map((d) => f(a, c, d)))); - - /// Chain multiple [IO] functions. - @override - IO<B> call<B>(covariant IO<B> chain) => flatMap((_) => chain); - - /// Chain the result of `then` to this [IO]. - @override - IO<B> andThen<B>(covariant IO<B> Function() then) => flatMap((_) => then()); - - /// Execute the IO function. - A run() => _run(); - - /// {@template fpdart_traverse_list_io} - /// Map each element in the list to an [IO] using the function `f`, - /// and collect the result in an `IO<List<B>>`. - /// {@endtemplate} - /// - /// Same as `IO.traverseList` but passing `index` in the map function. - static IO<List<B>> traverseListWithIndex<A, B>( - List<A> list, - IO<B> Function(A a, int i) f, - ) => - IO<List<B>>(() { - final resultList = <B>[]; - for (var i = 0; i < list.length; i++) { - resultList.add(f(list[i], i).run()); - } - return resultList; - }); - - /// {@macro fpdart_traverse_list_io} - /// - /// Same as `IO.traverseListWithIndex` but without `index` in the map function. - static IO<List<B>> traverseList<A, B>( - List<A> list, - IO<B> Function(A a) f, - ) => - traverseListWithIndex<A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_io} - /// Convert a `List<IO<A>>` to a single `IO<List<A>>`. - /// {@endtemplate} - static IO<List<A>> sequenceList<A>( - List<IO<A>> list, - ) => - traverseList(list, identity); - - @override - bool operator ==(Object other) => (other is IO) && other._run == _run; - - @override - int get hashCode => _run.hashCode; -} diff --git a/packages/fpdart/lib/src/io_either.dart b/packages/fpdart/lib/src/io_either.dart deleted file mode 100644 index 06bcba1..0000000 --- a/packages/fpdart/lib/src/io_either.dart +++ /dev/null @@ -1,287 +0,0 @@ -import 'either.dart'; -import 'function.dart'; -import 'io.dart'; -import 'option.dart'; -import 'task_either.dart'; -import 'typeclass/alt.dart'; -import 'typeclass/applicative.dart'; -import 'typeclass/functor.dart'; -import 'typeclass/hkt.dart'; -import 'typeclass/monad.dart'; - -final class _IOEitherThrow<L> { - final L value; - const _IOEitherThrow(this.value); -} - -typedef DoAdapterIOEither<E> = A Function<A>(IOEither<E, A>); -DoAdapterIOEither<L> _doAdapter<L>() => - <A>(ioEither) => ioEither.run().getOrElse((l) => throw _IOEitherThrow(l)); - -typedef DoFunctionIOEither<L, A> = A Function(DoAdapterIOEither<L> $); - -/// Tag the [HKT2] interface for the actual [IOEither]. -abstract final class _IOEitherHKT {} - -/// `IOEither<L, R>` represents a **non-deterministic synchronous** computation that -/// can **cause side effects**, yields a value of type `R` or **can fail** by returning -/// a value of type `L`. -/// -/// If you want to represent a synchronous computation that may never fail, see [IO]. -final class IOEither<L, R> extends HKT2<_IOEitherHKT, L, R> - with - Functor2<_IOEitherHKT, L, R>, - Applicative2<_IOEitherHKT, L, R>, - Monad2<_IOEitherHKT, L, R>, - Alt2<_IOEitherHKT, L, R> { - final Either<L, R> Function() _run; - - /// Build an instance of [IOEither] from `Either<L, R> Function()`. - const IOEither(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory IOEither.Do(DoFunctionIOEither<L, R> f) => IOEither(() { - try { - return Either.of(f(_doAdapter<L>())); - } on _IOEitherThrow<L> catch (e) { - return Either.left(e.value); - } - }); - - /// Used to chain multiple functions that return a [IOEither]. - /// - /// You can extract the value of every [Right] in the chain without - /// handling all possible missing cases. - /// If running any of the IOs in the chain returns [Left], the result is [Left]. - @override - IOEither<L, C> flatMap<C>(covariant IOEither<L, C> Function(R r) f) => - IOEither( - () => run().match( - (l) => Either.left(l), - (r) => f(r).run(), - ), - ); - - /// Chain a [TaskEither] with an [IOEither]. - /// - /// Allows to chain a function that returns a `Either<L, R>` ([IOEither]) to - /// a function that returns a `Future<Either<L, C>>` ([TaskEither]). - TaskEither<L, C> flatMapTask<C>(TaskEither<L, C> Function(R r) f) => - TaskEither( - () async => run().match( - (l) => Either.left(l), - (r) => f(r).run(), - ), - ); - - /// Convert this [IOEither] to [TaskEither]. - TaskEither<L, R> toTaskEither() => TaskEither(() async => run()); - - /// Returns a [IOEither] that returns a `Right(a)`. - @override - IOEither<L, C> pure<C>(C a) => IOEither(() => Right(a)); - - /// Change the return type of this [IOEither] based on its value of type `R` and the - /// value of type `C` of another [IOEither]. - @override - IOEither<L, D> map2<C, D>( - covariant IOEither<L, C> m1, D Function(R b, C c) f) => - flatMap((b) => m1.map((c) => f(b, c))); - - /// Change the return type of this [IOEither] based on its value of type `R`, the - /// value of type `C` of a second [IOEither], and the value of type `D` - /// of a third [IOEither]. - @override - IOEither<L, E> map3<C, D, E>(covariant IOEither<L, C> m1, - covariant IOEither<L, D> m2, E Function(R b, C c, D d) f) => - flatMap((b) => m1.flatMap((c) => m2.map((d) => f(b, c, d)))); - - /// If running this [IOEither] returns [Right], then return the result of calling `then`. - /// Otherwise return [Left]. - @override - IOEither<L, C> andThen<C>(covariant IOEither<L, C> Function() then) => - flatMap((_) => then()); - - /// If running this [IOEither] returns [Right], then change its value from type `R` to - /// type `C` using function `f`. - @override - IOEither<L, C> map<C>(C Function(R r) f) => ap(pure(f)); - - /// Change the value in the [Left] of [IOEither]. - IOEither<C, R> mapLeft<C>(C Function(L l) f) => IOEither( - () => (run()).match((l) => Either.left(f(l)), Either.of), - ); - - /// Define two functions to change both the [Left] and [Right] value of the - /// [IOEither]. - /// - /// {@macro fpdart_bimap_either} - IOEither<C, D> bimap<C, D>(C Function(L a) mLeft, D Function(R b) mRight) => - mapLeft(mLeft).map(mRight); - - /// Apply the function contained inside `a` to change the value on the [Right] from - /// type `R` to a value of type `C`. - @override - IOEither<L, C> ap<C>(covariant IOEither<L, C Function(R r)> a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - /// Change this [IOEither] from `IOEither<L, R>` to `IOEither<R, L>`. - IOEither<R, L> swap() => - IOEither(() => run().match((l) => Right(l), (r) => Left(r))); - - /// When this [IOEither] returns [Right], then return the current [IOEither]. - /// Otherwise return the result of `orElse`. - /// - /// Used to provide an **alt**ernative [IOEither] in case the current one returns [Left]. - @override - IOEither<L, R> alt(covariant IOEither<L, R> Function() orElse) => - IOEither(() => run().match((_) => orElse().run(), right)); - - /// Chain multiple functions having the same left type `L`. - @override - IOEither<L, C> call<C>(covariant IOEither<L, C> chain) => - flatMap((_) => chain); - - /// If `f` applied on this [IOEither] as [Right] returns `true`, then return this [IOEither]. - /// If it returns `false`, return the result of `onFalse` in a [Left]. - IOEither<L, R> filterOrElse(bool Function(R r) f, L Function(R r) onFalse) => - flatMap((r) => f(r) ? IOEither.of(r) : IOEither.left(onFalse(r))); - - /// When this [IOEither] returns a [Left] then return the result of `orElse`. - /// Otherwise return this [IOEither]. - IOEither<TL, R> orElse<TL>(IOEither<TL, R> Function(L l) orElse) => - IOEither(() => run().match( - (l) => orElse(l).run(), (r) => IOEither<TL, R>.right(r).run())); - - /// Convert this [IOEither] to a [IO]. - /// - /// The IO returns a [Right] when [IOEither] returns [Right]. - /// Otherwise map the type `L` of [IOEither] to type `R` by calling `orElse`. - IO<R> getOrElse(R Function(L l) orElse) => - IO(() => run().match(orElse, identity)); - - /// Pattern matching to convert a [IOEither] to a [IO]. - /// - /// Execute `onLeft` when running this [IOEither] returns a [Left]. - /// Otherwise execute `onRight`. - IO<A> match<A>(A Function(L l) onLeft, A Function(R r) onRight) => - IO(() => run().match(onLeft, onRight)); - - /// Chain a request that returns another [IOEither], execute it, ignore - /// the result, and return the same value as the current [IOEither]. - @override - IOEither<L, R> chainFirst<C>( - covariant IOEither<L, C> Function(R b) chain, - ) => - flatMap((b) => chain(b).map((c) => b).orElse((l) => IOEither.right(b))); - - /// Run the IO and return a `Either<L, R>`. - Either<L, R> run() => _run(); - - /// Build a [IOEither] that returns a `Right(r)`. - /// - /// Same of `IOEither.right`. - factory IOEither.of(R r) => IOEither(() => Either.of(r)); - - /// Flat a [IOEither] contained inside another [IOEither] to be a single [IOEither]. - factory IOEither.flatten(IOEither<L, IOEither<L, R>> ioEither) => - ioEither.flatMap(identity); - - /// Build a [IOEither] that returns a `Right(right)`. - /// - /// Same of `IOEither.of`. - factory IOEither.right(R right) => IOEither(() => Either.of(right)); - - /// Build a [IOEither] that returns a `Left(left)`. - factory IOEither.left(L left) => IOEither(() => Left(left)); - - /// Build a [IOEither] that returns a [Left] containing the result of running `io`. - factory IOEither.leftIO(IO<L> io) => IOEither(() => Either.left(io.run())); - - /// Build a [IOEither] that returns a [Right] containing the result of running `io`. - /// - /// Same of `IOEither.fromIO` - factory IOEither.rightIO(IO<R> io) => IOEither(() => Right(io.run())); - - /// Build a [IOEither] from the result of running `io`. - /// - /// Same of `IOEither.rightIO` - factory IOEither.fromIO(IO<R> io) => IOEither(() => Right(io.run())); - - /// When calling `predicate` with `value` returns `true`, then running [IOEither] returns `Right(value)`. - /// Otherwise return `onFalse`. - factory IOEither.fromPredicate( - R value, bool Function(R a) predicate, L Function(R a) onFalse) => - IOEither(() => predicate(value) ? Right(value) : Left(onFalse(value))); - - /// Build a [IOEither] from `option`. - /// - /// When `option` is [Some], then return [Right] when - /// running [IOEither]. Otherwise return `onNone`. - factory IOEither.fromOption(Option<R> option, L Function() onNone) => - IOEither(() => option.match( - () => Left(onNone()), - Right.new, - )); - - /// Build a [IOEither] that returns `either`. - factory IOEither.fromEither(Either<L, R> either) => IOEither(() => either); - - /// If `r` is `null`, then return the result of `onNull` in [Left]. - /// Otherwise return `Right(r)`. - factory IOEither.fromNullable(R? r, L Function() onNull) => - Either.fromNullable(r, onNull).toIOEither(); - - /// Converts a [Function] that may throw to a [Function] that never throws - /// but returns a [Either] instead. - /// - /// Used to handle asynchronous computations that may throw using [Either]. - factory IOEither.tryCatch(R Function() run, - L Function(Object error, StackTrace stackTrace) onError) => - IOEither<L, R>(() { - try { - return Right<L, R>(run()); - } catch (error, stack) { - return Left<L, R>(onError(error, stack)); - } - }); - - /// {@template fpdart_traverse_list_io_either} - /// Map each element in the list to a [IOEither] using the function `f`, - /// and collect the result in an `IOEither<E, List<B>>`. - /// {@endtemplate} - /// - /// Same as `IOEither.traverseList` but passing `index` in the map function. - static IOEither<E, List<B>> traverseListWithIndex<E, A, B>( - List<A> list, - IOEither<E, B> Function(A a, int i) f, - ) => - IOEither<E, List<B>>( - () => Either.sequenceList( - IO - .traverseListWithIndex<A, Either<E, B>>( - list, - (a, i) => IO(() => f(a, i).run()), - ) - .run(), - ), - ); - - /// {@macro fpdart_traverse_list_io_either} - /// - /// Same as `IOEither.traverseListWithIndex` but without `index` in the map function. - static IOEither<E, List<B>> traverseList<E, A, B>( - List<A> list, - IOEither<E, B> Function(A a) f, - ) => - traverseListWithIndex<E, A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_io_either} - /// Convert a `List<IOEither<E, A>>` to a single `IOEither<E, List<A>>`. - /// {@endtemplate} - static IOEither<E, List<A>> sequenceList<E, A>( - List<IOEither<E, A>> list, - ) => - traverseList(list, identity); -} diff --git a/packages/fpdart/lib/src/io_option.dart b/packages/fpdart/lib/src/io_option.dart deleted file mode 100644 index 385a214..0000000 --- a/packages/fpdart/lib/src/io_option.dart +++ /dev/null @@ -1,258 +0,0 @@ -import 'either.dart'; -import 'extension/option_extension.dart'; -import 'function.dart'; -import 'io.dart'; -import 'io_either.dart'; -import 'option.dart'; -import 'task_either.dart'; -import 'task_option.dart'; -import 'typeclass/alt.dart'; -import 'typeclass/applicative.dart'; -import 'typeclass/functor.dart'; -import 'typeclass/hkt.dart'; -import 'typeclass/monad.dart'; - -final class _IOOptionThrow { - const _IOOptionThrow(); -} - -typedef DoAdapterIOOption = A Function<A>(IOOption<A>); -A _doAdapter<A>(IOOption<A> iOOption) => iOOption.run().getOrElse( - () => throw const _IOOptionThrow(), - ); - -typedef DoFunctionIOOption<A> = A Function(DoAdapterIOOption $); - -/// Tag the [HKT] interface for the actual [IOOption]. -abstract final class _IOOptionHKT {} - -/// `IOOption<R>` represents an **synchronous** computation that -/// may fails yielding a [None] or returns a `Some(R)` when successful. -/// -/// If you want to represent an synchronous computation that never fails, see [IO]. -/// -/// If you want to represent an synchronous computation that returns an object when it fails, -/// see [IOEither]. -final class IOOption<R> extends HKT<_IOOptionHKT, R> - with - Functor<_IOOptionHKT, R>, - Applicative<_IOOptionHKT, R>, - Monad<_IOOptionHKT, R>, - Alt<_IOOptionHKT, R> { - final Option<R> Function() _run; - - /// Build a [IOOption] from a function returning a `Option<R>`. - const IOOption(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory IOOption.Do(DoFunctionIOOption<R> f) => IOOption(() { - try { - return Option.of(f(_doAdapter)); - } on _IOOptionThrow catch (_) { - return const Option.none(); - } - }); - - /// Used to chain multiple functions that return a [IOOption]. - /// - /// You can extract the value of every [Some] in the chain without - /// handling all possible missing cases. - /// If running any of the functions in the chain returns [None], the result is [None]. - @override - IOOption<C> flatMap<C>(covariant IOOption<C> Function(R r) f) => - IOOption(() => run().match( - Option.none, - (r) => f(r).run(), - )); - - /// Returns a [IOOption] that returns `Some(c)`. - @override - IOOption<C> pure<C>(C c) => IOOption(() => Option.of(c)); - - /// Change the return type of this [IOOption] based on its value of type `R` and the - /// value of type `C` of another [IOOption]. - @override - IOOption<D> map2<C, D>(covariant IOOption<C> m1, D Function(R b, C c) f) => - flatMap((b) => m1.map((c) => f(b, c))); - - /// Change the return type of this [IOOption] based on its value of type `R`, the - /// value of type `C` of a second [IOOption], and the value of type `D` - /// of a third [IOOption]. - @override - IOOption<E> map3<C, D, E>(covariant IOOption<C> m1, covariant IOOption<D> m2, - E Function(R b, C c, D d) f) => - flatMap((b) => m1.flatMap((c) => m2.map((d) => f(b, c, d)))); - - /// If running this [IOOption] returns [Some], then return the result of calling `then`. - /// Otherwise return [None]. - @override - IOOption<C> andThen<C>(covariant IOOption<C> Function() then) => - flatMap((_) => then()); - - /// Chain multiple [IOOption] functions. - @override - IOOption<B> call<B>(covariant IOOption<B> chain) => flatMap((_) => chain); - - /// If running this [IOOption] returns [Some], then change its value from type `R` to - /// type `C` using function `f`. - @override - IOOption<C> map<C>(C Function(R r) f) => ap(pure(f)); - - /// Apply the function contained inside `a` to change the value on the [Some] from - /// type `R` to a value of type `C`. - @override - IOOption<C> ap<C>(covariant IOOption<C Function(R r)> a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - /// When this [IOOption] returns [Some], then return the current [IOOption]. - /// Otherwise return the result of `orElse`. - /// - /// Used to provide an **alt**ernative [IOOption] in case the current one returns [None]. - @override - IOOption<R> alt(covariant IOOption<R> Function() orElse) => - IOOption(() => run().match( - () => orElse().run(), - some, - )); - - /// When this [IOOption] returns a [None] then return the result of `orElse`. - /// Otherwise return this [IOOption]. - IOOption<R> orElse<TL>(IOOption<R> Function() orElse) => - IOOption(() => run().match( - () => orElse().run(), - (r) => IOOption<R>.some(r).run(), - )); - - /// Extract the result of this [IOOption] into a [IO]. - /// - /// The IO returns a [Some] when [IOOption] returns [Some]. - /// Otherwise map the type `L` of [IOOption] to type `R` by calling `orElse`. - IO<R> getOrElse(R Function() orElse) => IO(() => run().match( - orElse, - identity, - )); - - /// Pattern matching to convert a [IOOption] to a [IO]. - /// - /// Execute `onNone` when running this [IOOption] returns a [None]. - /// Otherwise execute `onSome`. - IO<A> match<A>(A Function() onNone, A Function(R r) onSome) => - IO(() => run().match( - onNone, - onSome, - )); - - /// Run the IO and return a `Option<R>`. - Option<R> run() => _run(); - - /// Convert this [IOOption] to [IOEither]. - /// - /// If the value inside [IOOption] is [None], then use `onNone` to convert it - /// to a value of type `L`. - IOEither<L, R> toIOEither<L>(L Function() onNone) => - IOEither(() => Either.fromOption(run(), onNone)); - - /// Convert this [IOOption] to [TaskOption]. - TaskOption<R> toTaskOption<L>() => TaskOption(() async => run()); - - /// Convert this [IOOption] to [TaskEither]. - /// - /// If the value inside [IOOption] is [None], then use `onNone` to convert it - /// to a value of type `L`. - TaskEither<L, R> toTaskEither<L>(L Function() onNone) => - TaskEither(() async => Either.fromOption(run(), onNone)); - - /// Build a [IOOption] that returns a `Some(r)`. - /// - /// Same of `IOOption.some`. - factory IOOption.of(R r) => IOOption(() => Option.of(r)); - - /// Flat a [IOOption] contained inside another [IOOption] to be a single [IOOption]. - factory IOOption.flatten(IOOption<IOOption<R>> ioOption) => - ioOption.flatMap(identity); - - /// Build a [IOOption] that returns a `Some(r)`. - /// - /// Same of `IOOption.of`. - factory IOOption.some(R r) => IOOption(() => Option.of(r)); - - /// Build a [IOOption] that returns a [None]. - factory IOOption.none() => IOOption(() => const Option.none()); - - /// If `r` is `null`, then return [None]. - /// Otherwise return `Right(r)`. - factory IOOption.fromNullable(R? r) => Option.fromNullable(r).toIOOption(); - - /// When calling `predicate` with `value` returns `true`, then running [IOOption] returns `Some(value)`. - /// Otherwise return [None]. - factory IOOption.fromPredicate(R value, bool Function(R a) predicate) => - IOOption( - () => predicate(value) ? Option.of(value) : const Option.none(), - ); - - /// Converts a function that may throw to a function that never throws - /// but returns a [Option] instead. - /// - /// Used to handle synchronous computations that may throw using [Option]. - factory IOOption.tryCatch(R Function() run) => IOOption<R>(() { - try { - return Option.of(run()); - } catch (_) { - return const Option.none(); - } - }); - - /// {@template fpdart_traverse_list_io_option} - /// Map each element in the list to a [IOOption] using the function `f`, - /// and collect the result in an `IOOption<List<B>>`. - /// {@endtemplate} - /// - /// Same as `IOOption.traverseList` but passing `index` in the map function. - static IOOption<List<B>> traverseListWithIndex<A, B>( - List<A> list, - IOOption<B> Function(A a, int i) f, - ) => - IOOption<List<B>>( - () => Option.sequenceList( - IO - .traverseListWithIndex<A, Option<B>>( - list, - (a, i) => IO(() => f(a, i).run()), - ) - .run(), - ), - ); - - /// {@macro fpdart_traverse_list_io_option} - /// - /// Same as `IOOption.traverseListWithIndex` but without `index` in the map function. - static IOOption<List<B>> traverseList<A, B>( - List<A> list, - IOOption<B> Function(A a) f, - ) => - traverseListWithIndex<A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_io_option} - /// Convert a `List<IOOption<A>>` to a single `IOOption<List<A>>`. - /// {@endtemplate} - static IOOption<List<A>> sequenceList<A>( - List<IOOption<A>> list, - ) => - traverseList(list, identity); - - /// Build a [IOOption] from `either` that returns [None] when - /// `either` is [Left], otherwise it returns [Some]. - static IOOption<R> fromEither<L, R>(Either<L, R> either) => - IOOption(() => either.match((_) => const Option.none(), some)); - - /// Converts a function that may throw to a function that never throws - /// but returns a [Option] instead. - /// - /// Used to handle synchronous computations that may throw using [Option]. - /// - /// It wraps the `IOOption.tryCatch` factory to make chaining with `flatMap` - /// easier. - static IOOption<R> Function(A a) tryCatchK<R, A>(R Function(A a) run) => - (a) => IOOption.tryCatch(() => run(a)); -} diff --git a/packages/fpdart/lib/src/io_ref.dart b/packages/fpdart/lib/src/io_ref.dart deleted file mode 100644 index 6010302..0000000 --- a/packages/fpdart/lib/src/io_ref.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'io.dart'; -import 'typeclass/eq.dart'; -import 'typedef.dart'; -import 'unit.dart'; - -/// Mutable reference in the [IO] monad. -/// -/// Allows having a reference that can be read and mutated inside the [IO] -/// monad. Can be used in conjunction with a closure to preserve a state across -/// multiple [IO] function calls, or in any other case where code is run -/// inside an IO monad. -/// -/// In most cases, the [State] monad should be used, and [IORef] must be -/// viewed as a last resort, as it holds a mutable field inside itself that can -/// be modified inside of the [IO] monad. -final class IORef<T> { - T _value; - - IORef._(this._value); - - /// {@template create_io_ref} - /// Creates a new IORef inside an [IO] monad with a given initial value. - /// {@endtemplate} - static IO<IORef<T>> create<T>(T initial) => IO(() => IORef._(initial)); - - /// {@template read_io_ref} - /// Extracts a current value of the [IORef] and returns it inside the - /// [IO] monad. - /// {@endtemplate} - IO<T> read() => IO.of(_value); - - /// {@template write_io_ref} - /// Writes the given value to the [IORef] and returns a [Unit] inside the - /// [IO] monad. - /// {@endtemplate} - IO<Unit> write(T value) => IO(() => _value = value).map((_) => unit); - - /// {@template modify_io_ref} - /// Works almost identical to the [write] method, but instead of taking - /// a value that needs to be written, takes an [Endo] function, applies the - /// [IORef]'s current value to it and writes the result to the [IORef]. - /// {@endtemplate} - IO<Unit> modify(Endo<T> update) => read().map(update).flatMap(write); -} - -/// [Eq] instance to compare [IORef]s using pointer equality -final ioRefEq = Eq.instance<IORef<Object?>>((a, b) => identical(a, b)); - -/// {@macro create_io_ref} -IO<IORef<T>> newIORef<T>(T initial) => IORef.create(initial); - -/// {@macro read_io_ref} -IO<T> readIORef<T>(IORef<T> ref) => ref.read(); - -/// {@macro write_io_ref} -IO<Unit> writeIORef<T>(T value, IORef<T> ref) => ref.write(value); - -/// {@template io_ref_curried_version} -/// A curried version of the -/// {@endtemplate} -/// [writeIORef] -IO<Unit> Function(IORef<T> ref) writeIORefC<T>(T value) => - (ref) => ref.write(value); - -/// {@macro modify_io_ref} -IO<Unit> modifyIORef<T>(Endo<T> update, IORef<T> ref) => ref.modify(update); - -/// {@macro io_ref_curried_version} -/// [modifyIORef] -IO<Unit> Function(IORef<T> ref) modifyIORefC<T>(Endo<T> update) => - (ref) => ref.modify(update); diff --git a/packages/fpdart/lib/src/n_either.dart b/packages/fpdart/lib/src/n_either.dart deleted file mode 100644 index dc9a7b8..0000000 --- a/packages/fpdart/lib/src/n_either.dart +++ /dev/null @@ -1,62 +0,0 @@ -part of "effect.dart"; - -sealed class NEither<L, R> extends IEffect<Never, L, R> { - const NEither(); - - NEither<L, C> flatMap<C>(covariant NEither<L, C> Function(R r) f) { - return switch (this) { - NLeft(value: final value) => NLeft(value), - NRight(value: final value) => f(value), - }; - } - - NEither<L, V> ap<V>( - covariant NEither<L, V Function(R r)> f, - ) => - f.flatMap( - (f) => flatMap( - (v) => NRight(f(v)), - ), - ); - - NEither<L, V> map<V>(V Function(R r) f) => ap(NRight(f)); - - Effect<V, L, R> provide<V>() => Effect._( - (env) => switch (this) { - NLeft(value: final value) => Exit.failure(value), - NRight(value: final value) => Exit.success(value), - }, - ); - - NEither<C, R> mapLeft<C>(C Function(L l) f) => switch (this) { - NLeft(value: final value) => NLeft(f(value)), - NRight(value: final value) => NRight(value), - }; -} - -final class NRight<L, R> extends NEither<L, R> { - final R value; - const NRight(this.value); - - @override - Effect<Never, L, R> get asEffect => Effect.succeed(value); - - NEither<L, C> andThen<C>(covariant NEither<L, C> Function() then) => then(); - - NEither<C, R> orElse<C>(covariant NEither<C, R> Function(L l) orElse) => - NRight(value); -} - -final class NLeft<L, R> extends NEither<L, R> { - final L value; - const NLeft(this.value); - - @override - Effect<Never, L, R> get asEffect => Effect.fail(value); - - NEither<L, C> andThen<C>(covariant NEither<L, C> Function() then) => - NLeft(value); - - NEither<C, R> orElse<C>(covariant NEither<C, R> Function(L l) orElse) => - orElse(value); -} diff --git a/packages/fpdart/lib/src/n_option.dart b/packages/fpdart/lib/src/n_option.dart deleted file mode 100644 index 6bad6b0..0000000 --- a/packages/fpdart/lib/src/n_option.dart +++ /dev/null @@ -1,53 +0,0 @@ -part of "effect.dart"; - -sealed class NOption<R> extends IEffect<Never, Never, R> { - const NOption(); - - NOption<C> flatMap<C>(covariant NOption<C> Function(R r) f) { - return switch (this) { - NNone() => NNone<Never>(), - NSome(value: final value) => f(value), - }; - } - - NOption<V> ap<V>( - covariant NOption<V Function(R r)> f, - ) => - f.flatMap( - (f) => flatMap( - (v) => NSome(f(v)), - ), - ); - - NOption<V> map<V>(V Function(R r) f) => ap(NSome(f)); - - Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect._( - (env) => switch (this) { - NNone() => Exit.failure(onNone()), - NSome(value: final value) => Exit.success(value), - }, - ); -} - -final class NSome<R> extends NOption<R> { - final R value; - const NSome(this.value); - - @override - Effect<Never, Never, R> get asEffect => Effect.succeed(value); - - NOption<C> andThen<C>(covariant NOption<C> Function() then) => then(); -} - -final class NNone<R> extends NOption<Never> { - const NNone(); - - @override - @internal - - /// **This will always throw, don't use it!** - // ignore: cast_from_null_always_fails - Effect<Never, Never, Never> get asEffect => Effect.fail(null as Never); - - NOption<C> andThen<C>(covariant NOption<C> Function() then) => this; -} diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index 139e3be..6525bcb 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -1,617 +1,53 @@ -import 'either.dart'; -import 'extension/option_extension.dart'; -import 'function.dart'; -import 'io_option.dart'; -import 'task_option.dart'; -import 'typeclass/applicative.dart'; -import 'typeclass/eq.dart'; -import 'typeclass/extend.dart'; -import 'typeclass/filterable.dart'; -import 'typeclass/functor.dart'; -import 'typeclass/hkt.dart'; -import 'typeclass/monad.dart'; -import 'typeclass/monoid.dart'; -import 'typeclass/order.dart'; -import 'typeclass/semigroup.dart'; +part of "effect.dart"; -/// Return a `Some(t)`. -/// -/// Shortcut for `Option.of(r)`. -Option<T> some<T>(T t) => Some(t); - -/// Return a [None]. -/// -/// Shortcut for `Option.none()`. -Option<T> none<T>() => const Option.none(); - -/// Return [None] if `t` is `null`, [Some] otherwise. -/// -/// Same as initializing `Option.fromNullable(t)`. -Option<T> optionOf<T>(T? t) => Option.fromNullable(t); - -/// Return [Some] of `value` when `predicate` applied to `value` returns `true`, -/// [None] otherwise. -/// -/// Same as initializing `Option.fromPredicate(value, predicate)`. -Option<T> option<T>(T value, bool Function(T) predicate) => - Option.fromPredicate(value, predicate); - -final class _OptionThrow { - const _OptionThrow(); -} - -typedef DoAdapterOption = A Function<A>(Option<A>); -A _doAdapter<A>(Option<A> option) => - option.getOrElse(() => throw const _OptionThrow()); - -typedef DoFunctionOption<A> = A Function(DoAdapterOption $); - -/// Tag the [HKT] interface for the actual [Option]. -abstract final class _OptionHKT {} - -// `Option<T> implements Functor<OptionHKT, T>` expresses correctly the -// return type of the `map` function as `HKT<OptionHKT, B>`. -// This tells us that the actual type parameter changed from `T` to `B`, -// according to the types `T` and `B` of the callable we actually passed as a parameter of `map`. -// -// Moreover, it informs us that we are still considering an higher kinded type -// with respect to the `OptionHKT` tag - -/// A type that can contain a value of type `T` in a [Some] or no value with [None]. -/// -/// Used to represent type-safe missing values. Instead of using `null`, you define the type -/// to be [Option]. In this way, you are required by the type system to handle the case in which -/// the value is missing. -/// ```dart -/// final Option<String> mStr = Option.of('name'); -/// -/// /// Using [Option] you are required to specify every possible case. -/// /// The type system helps you to find and define edge-cases and avoid errors. -/// mStr.match( -/// () => print('I have no string to print 🤷♀️'), -/// printString, -/// ); -/// ``` -sealed class Option<T> extends HKT<_OptionHKT, T> - with - Functor<_OptionHKT, T>, - Applicative<_OptionHKT, T>, - Monad<_OptionHKT, T>, - Extend<_OptionHKT, T>, - Filterable<_OptionHKT, T> { +sealed class Option<R> extends IEffect<Never, Never, R> { const Option(); - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory Option.Do(DoFunctionOption<T> f) { - try { - return Option.of(f(_doAdapter)); - } on _OptionThrow catch (_) { - return const Option.none(); - } - } - - /// Change the value of type `T` to a value of type `B` using function `f`. - /// ```dart - /// /// Change type `String` (`T`) to type `int` (`B`) - /// final Option<String> mStr = Option.of('name'); - /// final Option<int> mInt = mStr.map((a) => a.length); - /// ``` - /// 👇 - /// ```dart - /// [🥚].map((🥚) => 👨🍳(🥚)) -> [🍳] - /// [_].map((🥚) => 👨🍳(🥚)) -> [_] - /// ``` - @override - Option<B> map<B>(B Function(T t) f); - - /// Apply the function contained inside `a` to change the value of type `T` to - /// a value of type `B`. - /// - /// If `a` is [None], return [None]. - /// ```dart - /// final a = Option.of(10); - /// final b = Option.of(20); - /// - /// /// `map` takes one parameter [int] and returns `sumToDouble`. - /// /// We therefore have a function inside a [Option] that we want to - /// /// apply to another value! - /// final Option<double Function(int)> map = a.map( - /// (a) => (int b) => sumToDouble(a, b), - /// ); - /// - /// /// Using `ap`, we get the final `Option<double>` that we want 🚀 - /// final result = b.ap(map); - /// ``` - @override - Option<B> ap<B>(covariant Option<B Function(T t)> a) => a.match( - () => Option<B>.none(), - (f) => map(f), - ); - - /// Return a [Some] containing the value `b`. - @override - Option<B> pure<B>(B b) => Some(b); - - /// Used to chain multiple functions that return a [Option]. - /// - /// You can extract the value of every [Option] in the chain without - /// handling all possible missing cases. - /// If any of the functions in the chain returns [None], the result is [None]. - /// ```dart - /// /// Using `flatMap`, you can forget that the value may be missing and just - /// /// use it as if it was there. - /// /// - /// /// In case one of the values is actually missing, you will get a [None] - /// /// at the end of the chain ⛓ - /// final a = Option.of('name'); - /// final Option<double> result = a.flatMap( - /// (s) => stringToInt(s).flatMap( - /// (i) => intToDouble(i), - /// ), - /// ); - /// ``` - /// 👇 - /// ```dart - /// [😀].flatMap( - /// (😀) => [👻(😀)] - /// ) -> [😱] - /// - /// [😀].flatMap( - /// (😀) => [👻(😀)] - /// ).flatMap( - /// (😱) => [👨⚕️(😱)] - /// ) -> [🤕] - /// - /// [😀].flatMap( - /// (😀) => [_] - /// ).flatMap( - /// (_) => [👨⚕️(_)] - /// ) -> [_] - /// - /// [_].flatMap( - /// (😀) => [👻(😀)] - /// ) -> [_] - /// ``` - @override - Option<B> flatMap<B>(covariant Option<B> Function(T t) f); - - /// Return a new [Option] that calls [Option.fromNullable] on the result of of the given function [f]. - /// - /// ```dart - /// expect( - /// Option.of(123).flatMapNullable((_) => null), - /// Option.none(), - /// ); - /// - /// expect( - /// Option.of(123).flatMapNullable((_) => 456), - /// Option.of(456), - /// ); - /// ``` - Option<B> flatMapNullable<B>(B? Function(T t) f) => - flatMap((t) => Option.fromNullable(f(t))); - - /// Return a new [Option] that calls [Option.tryCatch] with the given function [f]. - /// - /// ```dart - /// expect( - /// Option.of(123).flatMapThrowable((_) => throw Exception()), - /// Option.of(123).flatMapThrowable((_) => 456), - /// Option.of(456), - /// ); - /// ``` - Option<B> flatMapThrowable<B>(B Function(T t) f) => - flatMap((t) => Option.tryCatch(() => f(t))); - - /// Change the value of [Option] from type `T` to type `Z` based on the - /// value of `Option<T>` using function `f`. - @override - Option<Z> extend<Z>(Z Function(Option<T> t) f); - - /// Wrap this [Option] inside another [Option]. - @override - Option<Option<T>> duplicate() => extend(identity); - - /// If this [Option] is a [Some] and calling `f` returns `true`, then return this [Some]. - /// Otherwise return [None]. - @override - Option<T> filter(bool Function(T t) f) => - flatMap((t) => f(t) ? this : const Option.none()); - - /// If this [Option] is a [Some] and calling `f` returns [Some], then return this [Some]. - /// Otherwise return [None]. - @override - Option<Z> filterMap<Z>(Option<Z> Function(T t) f); - - /// Return a record. If this [Option] is a [Some]: - /// - if `f` applied to its value returns `true`, then the tuple contains this [Option] as second value - /// - if `f` applied to its value returns `false`, then the tuple contains this [Option] as first value - /// Otherwise the tuple contains both [None]. - @override - (Option<T>, Option<T>) partition(bool Function(T t) f) => - (filter((a) => !f(a)), filter(f)); - - /// Return a record that contains as first value a [Some] when `f` returns [Left], - /// otherwise the [Some] will be the second value of the tuple. - @override - (Option<Z>, Option<Y>) partitionMap<Z, Y>(Either<Z, Y> Function(T t) f) => - Option.separate(map(f)); - - /// If this [Option] is a [Some], then return the result of calling `then`. - /// Otherwise return [None]. - /// ```dart - /// [🍌].andThen(() => [🍎]) -> [🍎] - /// [_].andThen(() => [🍎]) -> [_] - /// ``` - @override - Option<B> andThen<B>(covariant Option<B> Function() then) => - flatMap((_) => then()); - - /// Chain multiple [Option]s. - @override - Option<B> call<B>(covariant Option<B> chain) => flatMap((_) => chain); - - /// Change type of this [Option] based on its value of type `T` and the - /// value of type `C` of another [Option]. - @override - Option<D> map2<C, D>(covariant Option<C> mc, D Function(T t, C c) f) => - flatMap((a) => mc.map((c) => f(a, c))); - - /// Change type of this [Option] based on its value of type `T`, the - /// value of type `C` of a second [Option], and the value of type `D` - /// of a third [Option]. - @override - Option<E> map3<C, D, E>(covariant Option<C> mc, covariant Option<D> md, - E Function(T t, C c, D d) f) => - flatMap((a) => mc.flatMap((c) => md.map((d) => f(a, c, d)))); - - /// {@template fpdart_option_match} - /// Execute `onSome` when value is [Some], otherwise execute `onNone`. - /// {@endtemplate} - /// ```dart - /// [🍌].match(() => 🍎, (🍌) => 🍌 * 2) -> 🍌🍌 - /// [_].match(() => 🍎, (🍌) => 🍌 * 2) -> 🍎 - /// ``` - /// - /// Same as `fold`. - B match<B>(B Function() onNone, B Function(T t) onSome); - - /// {@macro fpdart_option_match} - /// ```dart - /// [🍌].fold(() => 🍎, (🍌) => 🍌 * 2) -> 🍌🍌 - /// [_].fold(() => 🍎, (🍌) => 🍌 * 2) -> 🍎 - /// ``` - /// - /// Same as `match`. - B fold<B>(B Function() onNone, B Function(T t) onSome) => - match(onNone, onSome); - - /// Return `true` when value is [Some]. - bool isSome(); - - /// Return `true` when value is [None]. - bool isNone(); - - /// Return value of type `T` when this [Option] is a [Some], `null` otherwise. - T? toNullable(); - - /// Build an [Either] from [Option]. - /// - /// Return [Right] when [Option] is [Some], otherwise [Left] containing - /// the result of calling `onLeft`. - Either<L, T> toEither<L>(L Function() onLeft) => match( - () => Left(onLeft()), - Right.new, - ); - - /// Convert this [Option] to a [IOOption]. - IOOption<T> toIOOption() => IOOption(() => this); - - /// Convert this [Option] to a [TaskOption]. - /// - /// Used to convert a sync context ([Option]) to an async context ([TaskOption]). - /// You should convert [Option] to [TaskOption] every time you need to - /// call an async ([Future]) function based on the value in [Option]. - TaskOption<T> toTaskOption() => TaskOption(() => Future.value(this)); - - /// {@template fpdart_traverse_list_option} - /// Map each element in the list to an [Option] using the function `f`, - /// and collect the result in an `Option<List<B>>`. - /// - /// If any mapped element of the list is [None], then the final result - /// will be [None]. - /// {@endtemplate} - /// - /// Same as `Option.traverseList` but passing `index` in the map function. - static Option<List<B>> traverseListWithIndex<A, B>( - List<A> list, - Option<B> Function(A a, int i) f, - ) { - final resultList = <B>[]; - for (var i = 0; i < list.length; i++) { - final o = f(list[i], i); - final r = o.match<B?>(() => null, identity); - if (r == null) return none(); - resultList.add(r); - } - - return some(resultList); + Option<C> flatMap<C>(covariant Option<C> Function(R r) f) { + return switch (this) { + None() => None(), + Some(value: final value) => f(value), + }; } - /// {@macro fpdart_traverse_list_option} - /// - /// Same as `Option.traverseListWithIndex` but without `index` in the map function. - static Option<List<B>> traverseList<A, B>( - List<A> list, - Option<B> Function(A a) f, - ) => - traverseListWithIndex<A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_option} - /// Convert a `List<Option<A>>` to a single `Option<List<A>>`. - /// - /// If any of the [Option] in the [List] is [None], then the result is [None]. - /// {@endtemplate} - static Option<List<A>> sequenceList<A>( - List<Option<A>> list, + Option<V> ap<V>( + covariant Option<V Function(R r)> f, ) => - traverseList(list, identity); - - /// Build a [Option] from a [Either] by returning [Some] when `either` is [Right], - /// [None] otherwise. - static Option<R> fromEither<L, R>(Either<L, R> either) => - either.match((_) => const Option.none(), (r) => Some(r)); - - /// {@template fpdart_safe_cast_option} - /// Safely cast a value to type `T`. - /// - /// If `value` is not of type `T`, then return a [None]. - /// {@endtemplate} - /// - /// Less strict version of `Option.safeCastStrict`, since `safeCast` - /// assumes the value to be `dynamic`. - /// - /// **Note**: Make sure to specify the type of [Option] (`Option<T>.safeCast` - /// instead of `Option.safeCast`), otherwise this will always return [Some]! - factory Option.safeCast(dynamic value) => - Option.safeCastStrict<T, dynamic>(value); - - /// {@macro fpdart_safe_cast_option} - /// - /// More strict version of `Option.safeCast`, in which also the **input value - /// type** must be specified (while in `Option.safeCast` the type is `dynamic`). - static Option<T> safeCastStrict<T, V>(V value) => - value is T ? Option<T>.of(value) : Option<T>.none(); - - /// Return [Some] of `value` when `predicate` applied to `value` returns `true`, - /// [None] otherwise. - factory Option.fromPredicate(T value, bool Function(T t) predicate) => - predicate(value) ? Some(value) : Option.none(); - - /// Return [Some] of type `B` by calling `f` with `value` when `predicate` applied to `value` is `true`, - /// `None` otherwise. - /// ```dart - /// /// If the value of `str` is not empty, then return a [Some] containing - /// /// the `length` of `str`, otherwise [None]. - /// Option.fromPredicateMap<String, int>( - /// str, - /// (str) => str.isNotEmpty, - /// (str) => str.length, - /// ); - /// ``` - static Option<B> fromPredicateMap<A, B>( - A value, bool Function(A a) predicate, B Function(A a) f) => - predicate(value) ? Some(f(value)) : Option.none(); - - /// Return a [None]. - const factory Option.none() = None; - - /// Return a `Some(a)`. - const factory Option.of(T t) = Some<T>; - - /// Flat a [Option] contained inside another [Option] to be a single [Option]. - factory Option.flatten(Option<Option<T>> m) => m.flatMap(identity); - - /// Return [None] if `a` is `null`, [Some] otherwise. - factory Option.fromNullable(T? t) => t == null ? Option.none() : Some(t); - - /// Try to run `f` and return `Some(a)` when no error are thrown, otherwise return `None`. - factory Option.tryCatch(T Function() f) { - try { - return Some(f()); - } catch (_) { - return const Option.none(); - } - } - - /// Return a record of [Option] from a `Option<Either<A, B>>`. - /// - /// The value on the left of the [Either] will be the first value of the tuple, - /// while the right value of the [Either] will be the second of the tuple. - static (Option<A>, Option<B>) separate<A, B>(Option<Either<A, B>> m) => - m.match( - () => (const Option.none(), const Option.none()), - (either) => (either.getLeft(), either.getRight()), + f.flatMap( + (f) => flatMap( + (v) => Some(f(v)), + ), ); - /// Build an `Eq<Option>` by comparing the values inside two [Option]. - /// - /// If both [Option] are [None], then calling `eqv` returns `true`. Otherwise, if both are [Some] - /// and their contained value is the same, then calling `eqv` returns `true`. - /// It returns `false` in all other cases. - static Eq<Option<T>> getEq<T>(Eq<T> eq) => Eq.instance((a1, a2) => - a1 == a2 || - a1 - .flatMap((j1) => a2.flatMap((j2) => Some(eq.eqv(j1, j2)))) - .getOrElse(() => false)); - - /// Build an instance of [Monoid] in which the `empty` value is [None] and the - /// `combine` function is based on the **first** [Option] if it is [Some], otherwise the second. - static Monoid<Option<T>> getFirstMonoid<T>() => - Monoid.instance(const Option.none(), (a1, a2) => a1.isNone() ? a2 : a1); - - /// Build an instance of [Monoid] in which the `empty` value is [None] and the - /// `combine` function is based on the **second** [Option] if it is [Some], otherwise the first. - static Monoid<Option<T>> getLastMonoid<T>() => - Monoid.instance(const Option.none(), (a1, a2) => a2.isNone() ? a1 : a2); + Option<V> map<V>(V Function(R r) f) => ap(Some(f)); - /// Build an instance of [Monoid] in which the `empty` value is [None] and the - /// `combine` function uses the given `semigroup` to combine the values of both [Option] - /// if they are both [Some]. - /// - /// If one of the [Option] is [None], then calling `combine` returns [None]. - static Monoid<Option<T>> getMonoid<T>(Semigroup<T> semigroup) => - Monoid.instance( - const Option.none(), - (a1, a2) => a1.flatMap((v1) => a2.flatMap( - (v2) => Some(semigroup.combine(v1, v2)), - ))); - - /// Return an [Order] to order instances of [Option]. - /// - /// Return `0` when the [Option]s are the same, otherwise uses the given `order` - /// to compare their values when they are both [Some]. - /// - /// Otherwise instances of [Some] comes before [None] in the ordering. - static Order<Option<T>> getOrder<T>(Order<T> order) => Order.from( - (a1, a2) => a1 == a2 - ? 0 - : a1 - .flatMap((v1) => a2.flatMap( - (v2) => Some(order.compare(v1, v2)), - )) - .getOrElse(() => a1.isSome() ? 1 : -1), + Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect._( + (env) => switch (this) { + None() => Exit.failure(onNone()), + Some(value: final value) => Exit.success(value), + }, ); - - /// Converts from Json. - /// - /// Json serialization support for `json_serializable` with `@JsonSerializable`. - factory Option.fromJson( - dynamic json, - T Function(dynamic json) fromJsonT, - ) => - json != null ? Option.tryCatch(() => fromJsonT(json)) : Option.none(); - - /// Converts to Json. - /// - /// Json serialization support for `json_serializable` with `@JsonSerializable`. - Object? toJson(Object? Function(T) toJsonT); } -class Some<T> extends Option<T> { - final T _value; - const Some(this._value); - - /// Extract value of type `T` inside the [Some]. - T get value => _value; - - @override - Option<D> map2<C, D>(covariant Option<C> mc, D Function(T t, C c) f) => - flatMap((a) => mc.map((c) => f(a, c))); - - @override - Option<E> map3<C, D, E>(covariant Option<C> mc, covariant Option<D> md, - E Function(T t, C c, D d) f) => - flatMap((a) => mc.flatMap((c) => md.map((d) => f(a, c, d)))); - - @override - Option<B> map<B>(B Function(T t) f) => Some(f(_value)); - - @override - Option<B> flatMap<B>(covariant Option<B> Function(T t) f) => f(_value); - - @override - B match<B>(B Function() onNone, B Function(T t) onSome) => onSome(_value); - - @override - Option<Z> extend<Z>(Z Function(Option<T> t) f) => Some(f(this)); - - @override - bool isSome() => true; - - @override - bool isNone() => false; - - @override - Option<T> filter(bool Function(T t) f) => f(_value) ? this : Option.none(); - - @override - Option<Z> filterMap<Z>(Option<Z> Function(T t) f) => f(_value).match( - () => const Option.none(), - Some.new, - ); - - @override - T toNullable() => _value; - - @override - Either<L, T> toEither<L>(L Function() onLeft) => Right(_value); +final class Some<R> extends Option<R> { + final R value; + const Some(this.value); @override - bool operator ==(Object other) => (other is Some) && other._value == _value; + Effect<Never, Never, R> get asEffect => Effect.succeed(value); - @override - int get hashCode => _value.hashCode; - - @override - String toString() => 'Some($_value)'; - - @override - Object? toJson(Object? Function(T p1) toJsonT) => toJsonT(_value); - - @override - TaskOption<T> toTaskOption() => TaskOption.of(_value); + Option<C> andThen<C>(covariant Option<C> Function() then) => then(); } -class None extends Option<Never> { +final class None extends Option<Never> { const None(); @override - Option<D> map2<C, D>(covariant Option<C> mc, D Function(Never t, C c) f) => - this; - - @override - Option<E> map3<C, D, E>( - covariant Option<C> mc, - covariant Option<D> md, - E Function(Never t, C c, D d) f, - ) => - this; - - @override - Option<B> map<B>(B Function(Never t) f) => this; - - @override - Option<B> flatMap<B>(covariant Option<B> Function(Never t) f) => this; - - @override - B match<B>(B Function() onNone, B Function(Never t) onSome) => onNone(); + @internal - @override - Option<Z> extend<Z>(Z Function(Option<Never> t) f) => const Option.none(); - - @override - bool isSome() => false; - - @override - bool isNone() => true; - - @override - Option<Z> filterMap<Z>(Option<Z> Function(Never t) f) => const Option.none(); - - @override - bool operator ==(Object other) => other is None; - - @override - Null toNullable() => null; - - @override - int get hashCode => 0; - - @override - String toString() => 'None'; + /// **This will always throw, don't use it!** + // ignore: cast_from_null_always_fails + Effect<Never, Never, Never> get asEffect => Effect.fail(null as Never); - @override - Object? toJson(Object? Function(Never p1) toJsonT) => null; + Option<C> andThen<C>(covariant Option<C> Function() then) => this; } diff --git a/packages/fpdart/lib/src/order.dart b/packages/fpdart/lib/src/order.dart new file mode 100644 index 0000000..da70af7 --- /dev/null +++ b/packages/fpdart/lib/src/order.dart @@ -0,0 +1,61 @@ +import 'ordering.dart'; + +class Order<T> { + final Ordering Function(T x, T y) compare; + const Order(this.compare); + + static Order<T> comparable<T extends Comparable<dynamic>>() => Order( + (x, y) => Ordering.fromOrder(x.compareTo(y)), + ); + + /// If `x < y`, return `x`, else return `y`. + T min(T x, T y) => lessThan(x, y) ? x : y; + + /// If `x > y`, return `x`, else return `y`. + T max(T x, T y) => greaterThan(x, y) ? x : y; + + /// Test whether `value` is between `min` and `max` (**inclusive**) + bool between(T min, T max, T value) => + greaterOrEqual(value, min) && lessOrEqual(value, max); + + /// Clamp `value` between `min` and `max` + T clamp(T min, T max, T value) => this.max(this.min(value, max), min); + + Order<A> mapInput<A>(T Function(A) map) => Order<A>( + (a1, a2) => compare(map(a1), map(a2)), + ); + + /// Return an [Order] reversed. + Order<T> get reverse => Order((x, y) => compare(y, x)); + + bool equal(T x, T y) => compare(x, y) == Ordering.equal; + + bool lessThan(T x, T y) => compare(x, y) == Ordering.lessThan; + + bool greaterThan(T x, T y) => compare(x, y) == Ordering.greaterThan; + + bool lessOrEqual(T x, T y) { + if (compare(x, y) case Ordering.lessThan || Ordering.equal) return true; + return false; + } + + bool greaterOrEqual(T x, T y) { + if (compare(x, y) case Ordering.equal || Ordering.greaterThan) return true; + return false; + } + + /// Convert `Order<B>` to an `Order<A>` using `f` + static Order<A> by<A, B>(B Function(A a) f, Order<B> ord) => + Order((x, y) => ord.compare(f(x), f(y))); + + /// Order using `first` and if two elements are equal falls back to `second` + static Order<A> whenEqual<A>(Order<A> first, Order<A> second) => Order( + (x, y) { + final ord = first.compare(x, y); + return ord == Ordering.equal ? second.compare(x, y) : ord; + }, + ); + + /// An `Order` instance that considers all instances to be equal + static Order<A> allEqual<A>() => Order((x, y) => Ordering.equal); +} diff --git a/packages/fpdart/lib/src/ordering.dart b/packages/fpdart/lib/src/ordering.dart new file mode 100644 index 0000000..e996c86 --- /dev/null +++ b/packages/fpdart/lib/src/ordering.dart @@ -0,0 +1,21 @@ +enum Ordering { + lessThan(-1), + equal(0), + greaterThan(1); + + const Ordering(this.order); + factory Ordering.fromOrder(int order) => switch (order) { + (< 0) => Ordering.lessThan, + (> 0) => Ordering.greaterThan, + _ => Ordering.equal + }; + + final int order; + + @override + String toString() => '$name($order)'; + + bool get isLessThan => this == Ordering.lessThan; + bool get isGreaterThan => this == Ordering.greaterThan; + bool get isEqual => this == Ordering.equal; +} diff --git a/packages/fpdart/lib/src/random.dart b/packages/fpdart/lib/src/random.dart deleted file mode 100644 index ccebc79..0000000 --- a/packages/fpdart/lib/src/random.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'dart:math'; - -import 'io.dart'; - -/// Generates a non-negative random floating point -/// value uniformly distributed in the range from 0.0, **inclusive**, to 1.0, **exclusive**. -/// -/// [IO] wrapper around dart `Random().nextDouble()`. -IO<double> get random => IO(() => Random().nextDouble()); - -/// Generates a random boolean value. -/// -/// [IO] wrapper around dart `Random().nextBool()`. -IO<bool> get randomBool => IO(() => Random().nextBool()); - -/// Generates a non-negative random integer uniformly distributed -/// in the range from `min`, **inclusive**, to `max`, **exclusive**. -IO<int> randomInt(int min, int max) => - IO(() => Random().nextInt(max - min) + min); diff --git a/packages/fpdart/lib/src/reader.dart b/packages/fpdart/lib/src/reader.dart deleted file mode 100644 index 047a4ac..0000000 --- a/packages/fpdart/lib/src/reader.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'function.dart'; -import 'typeclass/applicative.dart'; -import 'typeclass/functor.dart'; -import 'typeclass/hkt.dart'; -import 'typeclass/monad.dart'; - -/// Tag the [HKT2] interface for the actual [Reader]. -abstract final class ReaderHKT {} - -/// `Reader<R, A>` allows to read values `A` from a dependency/context `R` -/// without explicitly passing the dependency between multiple nested -/// function calls. -final class Reader<R, A> extends HKT2<ReaderHKT, R, A> - with - Functor2<ReaderHKT, R, A>, - Applicative2<ReaderHKT, R, A>, - Monad2<ReaderHKT, R, A> { - final A Function(R r) _read; - - /// Build a [Reader] given `A Function(R)`. - const Reader(this._read); - - /// Flat a [Reader] contained inside another [Reader] to be a single [Reader]. - factory Reader.flatten(Reader<R, Reader<R, A>> reader) => - reader.flatMap(identity); - - /// Apply the function contained inside `a` to change the value of type `A` to - /// a value of type `B`. - @override - Reader<R, C> ap<C>(covariant Reader<R, C Function(A a)> a) => - Reader((r) => a.run(r)(run(r))); - - /// Used to chain multiple functions that return a [Reader]. - @override - Reader<R, C> flatMap<C>(covariant Reader<R, C> Function(A a) f) => - Reader((r) => f(run(r)).run(r)); - - /// Return a [Reader] containing the value `c`. - @override - Reader<R, C> pure<C>(C c) => Reader((_) => c); - - /// Change the value of type `A` to a value of type `C` using function `f`. - @override - Reader<R, C> map<C>(C Function(A a) f) => ap(pure(f)); - - /// Change type of this [Reader] based on its value of type `A` and the - /// value of type `C` of another [Reader]. - @override - Reader<R, D> map2<C, D>(covariant Reader<R, C> m1, D Function(A a, C c) f) => - flatMap((a) => m1.map((c) => f(a, c))); - - /// Change type of this [Reader] based on its value of type `A`, the - /// value of type `C` of a second [Reader], and the value of type `D` - /// of a third [Reader]. - @override - Reader<R, E> map3<C, D, E>(covariant Reader<R, C> m1, - covariant Reader<R, D> m2, E Function(A a, C c, D d) f) => - flatMap((a) => m1.flatMap((c) => m2.map((d) => f(a, c, d)))); - - /// Chain the result of `then` to this [Reader]. - @override - Reader<R, C> andThen<C>(covariant Reader<R, C> Function() then) => - flatMap((_) => then()); - - /// Chain multiple functions having the reader `R`. - @override - Reader<R, C> call<C>(covariant Reader<R, C> chain) => flatMap((_) => chain); - - /// Compose the dependency `R` of this [Reader] to `reader`. - Reader<R, C> compose<C>(Reader<R, C> reader) => Reader((r) => reader.run(r)); - - /// Change dependency type of `Reader<R, A>` from `R` to `R1` after calling `run`. - Reader<R1, A> local<R1>(R Function(R1 context) f) => Reader((r) => run(f(r))); - - /// Read the current dependency `R`. - Reader<R, R> ask() => Reader(identity); - - /// Change reading function to `f` given context/dependency `R`. - Reader<R, A> asks(A Function(R r) f) => Reader(f); - - /// Chain a request that returns another [Reader], execute it, ignore - /// the result, and return the same value as the current [Reader]. - @override - Reader<R, A> chainFirst<C>( - covariant Reader<R, C> Function(A a) chain, - ) => - flatMap((a) => chain(a).map((c) => a)); - - /// Provide the value `R` (dependency) and extract result `A`. - A run(R r) => _read(r); - - @override - bool operator ==(Object other) => (other is Reader) && other._read == _read; - - @override - int get hashCode => _read.hashCode; -} diff --git a/packages/fpdart/lib/src/reader_task.dart b/packages/fpdart/lib/src/reader_task.dart deleted file mode 100644 index edd85cc..0000000 --- a/packages/fpdart/lib/src/reader_task.dart +++ /dev/null @@ -1,145 +0,0 @@ -import 'either.dart'; -import 'function.dart'; -import 'reader_task_either.dart'; -import 'typeclass/applicative.dart'; -import 'typeclass/functor.dart'; -import 'typeclass/hkt.dart'; -import 'typeclass/monad.dart'; - -typedef DoAdapterReaderTask<E> = Future<A> Function<A>(ReaderTask<E, A>); -DoAdapterReaderTask<E> _doAdapter<E>(E env) => - <A>(ReaderTask<E, A> task) => task.run(env); - -typedef DoFunctionReaderTask<E, A> = Future<A> Function( - DoAdapterReaderTask<E> $); - -/// Tag the [HKT] interface for the actual [ReaderTask]. -abstract final class _ReaderTaskHKT {} - -/// [ReaderTask] represents an asynchronous computation that yields a value of type `A` -/// from a context of type `E` and **never fails**. -/// -/// If you want to represent an asynchronous computation that may fail, see [ReaderTaskEither]. -final class ReaderTask<E, A> extends HKT2<_ReaderTaskHKT, E, A> - with - Functor2<_ReaderTaskHKT, E, A>, - Applicative2<_ReaderTaskHKT, E, A>, - Monad2<_ReaderTaskHKT, E, A> { - final Future<A> Function(E env) _run; - - /// Build a [ReaderTask] from a function returning a [Future] given `E`. - const ReaderTask(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory ReaderTask.Do(DoFunctionReaderTask<E, A> f) => - ReaderTask((env) => f(_doAdapter(env))); - - /// Build a [ReaderTask] that returns `a`. - factory ReaderTask.of(A a) => ReaderTask((_) async => a); - - /// Flat a [ReaderTask] contained inside another [ReaderTask] to be a single [ReaderTask]. - factory ReaderTask.flatten(ReaderTask<E, ReaderTask<E, A>> task) => - task.flatMap(identity); - - /// Apply the function contained inside `a` to change the value of type `A` to - /// a value of type `B`. - @override - ReaderTask<E, B> ap<B>(covariant ReaderTask<E, B Function(A a)> a) => - ReaderTask( - (env) => a.run(env).then( - (f) => run(env).then( - (v) => f(v), - ), - ), - ); - - /// Used to chain multiple functions that return a [ReaderTask]. - /// - /// You can extract the value inside the [ReaderTask] without actually running it. - @override - ReaderTask<E, B> flatMap<B>(covariant ReaderTask<E, B> Function(A a) f) => - ReaderTask( - (env) => run(env).then( - (v) => f(v).run(env), - ), - ); - - /// Return a [ReaderTask] returning the value `b`. - @override - ReaderTask<E, B> pure<B>(B a) => ReaderTask((_) async => a); - - /// Change the returning value of the [ReaderTask] from type - /// `A` to type `B` using `f`. - @override - ReaderTask<E, B> map<B>(B Function(A a) f) => ap(pure(f)); - - /// Change type of this [ReaderTask] based on its value of type `A` and the - /// value of type `C` of another [ReaderTask]. - @override - ReaderTask<E, D> map2<C, D>( - covariant ReaderTask<E, C> mc, D Function(A a, C c) f) => - flatMap( - (a) => mc.map( - (c) => f(a, c), - ), - ); - - /// Change type of this [ReaderTask] based on its value of type `A`, the - /// value of type `C` of a second [ReaderTask], and the value of type `D` - /// of a third [ReaderTask]. - @override - ReaderTask<E, F> map3<C, D, F>( - covariant ReaderTask<E, C> mc, - covariant ReaderTask<E, D> md, - F Function(A a, C c, D d) f, - ) => - flatMap( - (a) => mc.flatMap( - (c) => md.map( - (d) => f(a, c, d), - ), - ), - ); - - /// Run this [ReaderTask] and right after the [ReaderTask] returned from `then`. - @override - ReaderTask<E, B> andThen<B>(covariant ReaderTask<E, B> Function() then) => - flatMap( - (_) => then(), - ); - - @override - ReaderTask<E, A> chainFirst<B>( - covariant ReaderTask<E, B> Function(A a) chain, - ) => - flatMap( - (a) => chain(a).map((b) => a), - ); - - /// Chain multiple [ReaderTask] functions. - @override - ReaderTask<E, B> call<B>(covariant ReaderTask<E, B> chain) => flatMap( - (_) => chain, - ); - - /// Run the task and return a [Future]. - Future<A> run(E env) => _run(env); - - /// Convert this [ReaderTask] to [ReaderTaskEither]. - ReaderTaskEither<E, L, A> toReaderTaskEither<L>() => ReaderTaskEither( - (env) async => Either.of( - await run(env), - ), - ); - - /// Extract a value `A` given the current dependency `E`. - factory ReaderTask.asks(A Function(E) f) => ReaderTask( - (env) async => f(env), - ); - - /// Read the current dependency `E`. - static ReaderTask<E, E> ask<E, A>() => ReaderTask( - (env) async => env, - ); -} diff --git a/packages/fpdart/lib/src/reader_task_either.dart b/packages/fpdart/lib/src/reader_task_either.dart deleted file mode 100644 index ab4fe51..0000000 --- a/packages/fpdart/lib/src/reader_task_either.dart +++ /dev/null @@ -1,371 +0,0 @@ -import 'either.dart'; -import 'function.dart'; -import 'io.dart'; -import 'io_either.dart'; -import 'io_option.dart'; -import 'option.dart'; -import 'reader.dart'; -import 'reader_task.dart'; -import 'task.dart'; -import 'task_either.dart'; -import 'task_option.dart'; -import 'typeclass/alt.dart'; -import 'typeclass/applicative.dart'; -import 'typeclass/functor.dart'; -import 'typeclass/hkt.dart'; -import 'typeclass/monad.dart'; - -final class _ReaderTaskEitherThrow<L> { - final L value; - const _ReaderTaskEitherThrow(this.value); -} - -typedef DoAdapterReaderTaskEither<E, L> = Future<A> Function<A>( - ReaderTaskEither<E, L, A>); - -DoAdapterReaderTaskEither<E, L> _doAdapter<E, L>(E env) => - <A>(readerTaskEither) => readerTaskEither.run(env).then( - (either) => either.getOrElse((l) => throw _ReaderTaskEitherThrow(l)), - ); - -typedef DoFunctionReaderTaskEither<E, L, A> = Future<A> Function( - DoAdapterReaderTaskEither<E, L> $); - -/// Tag the [HKT3] interface for the actual [ReaderTaskEither]. -abstract final class _ReaderTaskEitherHKT {} - -/// `ReaderTaskEither<E, L, R>` represents an asynchronous computation (`Task`) that -/// either yields a value of type `R` or fails yielding an error of type `L` (`Either`), -/// that allows to read values from a dependency/context `E` (`Reader`). -/// -/// [ReaderTaskEither] models a complete program using `Reader` for dependency injection, -/// `Task` to perform asynchronous computation, and `Either` to handle errors. -final class ReaderTaskEither<E, L, R> - extends HKT3<_ReaderTaskEitherHKT, E, L, R> - with - Functor3<_ReaderTaskEitherHKT, E, L, R>, - Applicative3<_ReaderTaskEitherHKT, E, L, R>, - Monad3<_ReaderTaskEitherHKT, E, L, R>, - Alt3<_ReaderTaskEitherHKT, E, L, R> { - final Future<Either<L, R>> Function(E env) _run; - - /// Build a [ReaderTaskEither] from a function returning a `Future<Either<L, R>>`. - const ReaderTaskEither(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory ReaderTaskEither.Do(DoFunctionReaderTaskEither<E, L, R> f) => - ReaderTaskEither((env) async { - try { - return Either.of(await f(_doAdapter<E, L>(env))); - } on _ReaderTaskEitherThrow<L> catch (e) { - return Either.left(e.value); - } - }); - - /// Run the task given `E` and return a `Future<Either<L, R>>`. - Future<Either<L, R>> run(E env) => _run(env); - - /// Returns a [ReaderTaskEither] that returns a `Right(a)`. - @override - ReaderTaskEither<E, L, C> pure<C>(C a) => ReaderTaskEither( - (_) async => Right(a), - ); - - /// Used to chain multiple functions that return a [ReaderTaskEither]. - /// - /// You can extract the value of every [Right] in the chain without - /// handling all possible missing cases. - /// - /// If running any of the tasks in the chain returns [Left], the result is [Left]. - @override - ReaderTaskEither<E, L, C> flatMap<C>( - covariant ReaderTaskEither<E, L, C> Function(R r) f, - ) => - ReaderTaskEither((env) => run(env).then( - (either) async => either.match( - left, - (r) => f(r).run(env), - ), - )); - - /// Chain a function that takes the current value `R` inside this [TaskEither] - /// and returns [Either]. - /// - /// Similar to `flatMap`, but `f` returns [Either] instead of [TaskEither]. - ReaderTaskEither<E, L, C> flatMapTaskEither<C>( - TaskEither<L, C> Function(R r) f, - ) => - ReaderTaskEither((env) => run(env).then( - (either) async => either.match( - left, - (r) => f(r).run(), - ), - )); - - /// If running this [ReaderTaskEither] returns [Right], then return the result of calling `then`. - /// Otherwise return [Left]. - @override - ReaderTaskEither<E, L, C> andThen<C>( - covariant ReaderTaskEither<E, L, C> Function() then, - ) => - flatMap((_) => then()); - - /// If running this [ReaderTaskEither] returns [Right], then change its value from type `R` to - /// type `C` using function `f`. - @override - ReaderTaskEither<E, L, C> map<C>(C Function(R r) f) => ap(pure(f)); - - @override - ReaderTaskEither<E, L, N2> map2<N1, N2>( - covariant ReaderTaskEither<E, L, N1> m1, - N2 Function(R p1, N1 p2) f, - ) => - flatMap((b) => m1.map((c) => f(b, c))); - - @override - ReaderTaskEither<E, L, N3> map3<N1, N2, N3>( - covariant ReaderTaskEither<E, L, N1> m1, - covariant ReaderTaskEither<E, L, N2> m2, - N3 Function(R p1, N1 p2, N2 p3) f, - ) => - flatMap( - (b) => m1.flatMap((c) => m2.map((d) => f(b, c, d))), - ); - - /// Change the value in the [Left] of [ReaderTaskEither]. - ReaderTaskEither<E, C, R> mapLeft<C>(C Function(L l) f) => ReaderTaskEither( - (env) async => (await run(env)).match( - (l) => Either.left(f(l)), - Either.of, - ), - ); - - /// Define two functions to change both the [Left] and [Right] value of the - /// [ReaderTaskEither]. - ReaderTaskEither<E, C, D> bimap<C, D>( - C Function(L l) mLeft, - D Function(R r) mRight, - ) => - mapLeft(mLeft).map(mRight); - - /// Apply the function contained inside `a` to change the value on the [Right] from - /// type `R` to a value of type `C`. - @override - ReaderTaskEither<E, L, C> ap<C>( - covariant ReaderTaskEither<E, L, C Function(R r)> a, - ) => - a.flatMap( - (f) => flatMap( - (v) => pure(f(v)), - ), - ); - - @override - ReaderTaskEither<E, L, R> chainFirst<N1>( - covariant ReaderTaskEither<E, L, N1> Function(R p1) chain, - ) => - flatMap((b) => chain(b).map((c) => b)); - - /// Chain multiple functions having the same left type `L`. - @override - ReaderTaskEither<E, L, C> call<C>( - covariant ReaderTaskEither<E, L, C> chain, - ) => - flatMap((_) => chain); - - /// Change this [ReaderTaskEither] from `ReaderTaskEither<L, R>` to `ReaderTaskEither<R, L>`. - ReaderTaskEither<E, R, L> swap() => ReaderTaskEither( - (env) async => (await run(env)).match(right, left), - ); - - /// When this [ReaderTaskEither] returns [Right], then return the current [ReaderTaskEither]. - /// Otherwise return the result of `orElse`. - /// - /// Used to provide an **alt**ernative [ReaderTaskEither] in case the current one returns [Left]. - @override - ReaderTaskEither<E, L, R> alt( - covariant ReaderTaskEither<E, L, R> Function() orElse, - ) => - ReaderTaskEither( - (env) async => (await run(env)).match( - (_) => orElse().run(env), - right, - ), - ); - - /// If `f` applied on this [ReaderTaskEither] as [Right] returns `true`, then return this [ReaderTaskEither]. - /// If it returns `false`, return the result of `onFalse` in a [Left]. - ReaderTaskEither<E, L, R> filterOrElse( - bool Function(R r) f, - L Function(R r) onFalse, - ) => - flatMap( - (r) => - f(r) ? ReaderTaskEither.of(r) : ReaderTaskEither.left(onFalse(r)), - ); - - /// When this [ReaderTaskEither] returns a [Left] then return the result of `orElse`. - /// Otherwise return this [ReaderTaskEither]. - ReaderTaskEither<E, TL, R> orElse<TL>( - ReaderTaskEither<E, TL, R> Function(L l) orElse, - ) => - ReaderTaskEither((env) async => (await run(env)).match( - (l) => orElse(l).run(env), - (r) => ReaderTaskEither<E, TL, R>.of(r).run(env), - )); - - /// Convert this [ReaderTaskEither] to a [ReaderTask]. - /// - /// The task returns a [Right] when [ReaderTaskEither] returns [Right]. - /// Otherwise map the type `L` of [ReaderTaskEither] to type `R` by calling `orElse`. - ReaderTask<E, R> getOrElse(R Function(L left) orElse) => ReaderTask( - (env) async => (await run(env)).match( - orElse, - identity, - ), - ); - - /// Pattern matching to convert a [ReaderTaskEither] to a [ReaderTask]. - /// - /// Execute `onLeft` when running this [ReaderTaskEither] returns a [Left]. - /// Otherwise execute `onRight`. - ReaderTask<E, B> match<B>( - B Function(L left) onLeft, - B Function(R right) onRight, - ) => - ReaderTask( - (env) async => (await run(env)).match( - onLeft, - onRight, - ), - ); - - /// Flat a [ReaderTaskEither] contained inside another [ReaderTaskEither] to be a single [ReaderTaskEither]. - factory ReaderTaskEither.flatten( - ReaderTaskEither<E, L, ReaderTaskEither<E, L, R>> readerTaskEither, - ) => - readerTaskEither.flatMap(identity); - - /// Build a [ReaderTaskEither] that returns a [Right] containing the result of running `reader`. - factory ReaderTaskEither.fromReader(Reader<E, R> reader) => ReaderTaskEither( - (env) async => Right(reader.run(env)), - ); - - /// Build a [ReaderTaskEither] from a `Reader<E, L>`. - factory ReaderTaskEither.leftReader(Reader<E, L> reader) => ReaderTaskEither( - (env) async => Left(reader.run(env)), - ); - - /// Build a [ReaderTaskEither] that returns a `Left(left)`. - factory ReaderTaskEither.left(L left) => ReaderTaskEither( - (_) async => Left(left), - ); - - /// Build a [ReaderTaskEither] that returns a [Left] containing the result of running `task`. - factory ReaderTaskEither.leftTask(Task<L> task) => ReaderTaskEither( - (_) => task.run().then(left), - ); - - /// Build a [ReaderTaskEither] that returns a [Right] containing the result of running `task`. - factory ReaderTaskEither.fromTask(Task<R> task) => ReaderTaskEither( - (_) async => Right(await task.run()), - ); - - /// Build a [ReaderTaskEither] that returns a [Right] containing the result of running `task`, - /// or the result of `onNone` if `task` is [Left]. - factory ReaderTaskEither.fromTaskOption( - TaskOption<R> task, - L Function() onNone, - ) => - ReaderTaskEither( - (_) async => Either.fromOption(await task.run(), onNone), - ); - - /// Build a [ReaderTaskEither] that returns a [Right] containing the result of running `task`. - factory ReaderTaskEither.fromTaskEither(TaskEither<L, R> task) => - ReaderTaskEither( - (_) async => task.run(), - ); - - /// Build a [ReaderTaskEither] that returns a [Right] containing the result of running `io`. - factory ReaderTaskEither.fromIO(IO<R> io) => ReaderTaskEither( - (_) async => Right(io.run()), - ); - - /// Build a [ReaderTaskEither] that returns a [Right] containing the result of running `io`, - /// or the result of `onNone` if `io` is [Left]. - factory ReaderTaskEither.fromIOOption( - IOOption<R> io, - L Function() onNone, - ) => - ReaderTaskEither( - (_) async => Either.fromOption(io.run(), onNone), - ); - - /// Build a [ReaderTaskEither] that returns a [Right] containing the result of running `io`. - factory ReaderTaskEither.fromIOEither(IOEither<L, R> io) => ReaderTaskEither( - (_) async => io.run(), - ); - - /// {@template fpdart_from_nullable_reader_task_either} - /// If `r` is `null`, then return the result of `onNull` in [Left]. - /// Otherwise return `Right(r)`. - /// {@endtemplate} - factory ReaderTaskEither.fromNullable(R? r, L Function() onNull) => - ReaderTaskEither( - (_) async => Either.fromNullable(r, onNull), - ); - - /// {@macro fpdart_from_nullable_reader_task_either} - factory ReaderTaskEither.fromNullableAsync(R? r, Task<L> onNull) => - ReaderTaskEither( - (_) async => r != null ? Either.of(r) : Either.left(await onNull.run()), - ); - - /// Build a [ReaderTaskEither] from `option`. - /// - /// When `option` is [Some], then return [Right] when - /// running [ReaderTaskEither]. Otherwise return `onNone`. - factory ReaderTaskEither.fromOption(Option<R> option, L Function() onNone) => - ReaderTaskEither((_) async => option.match( - () => Left(onNone()), - Right.new, - )); - - /// Build a [ReaderTaskEither] that returns `either`. - factory ReaderTaskEither.fromEither(Either<L, R> either) => - ReaderTaskEither((_) async => either); - - /// Build a [ReaderTaskEither] that returns a `Right(r)`. - factory ReaderTaskEither.of(R r) => ReaderTaskEither( - (_) async => Either.of(r), - ); - - /// 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`. - factory ReaderTaskEither.tryCatch( - Future<R> Function(E) run, - L Function(Object error, StackTrace stackTrace) onError, - ) => - ReaderTaskEither<E, L, R>((env) async { - try { - return Right<L, R>(await run(env)); - } catch (error, stack) { - return Left<L, R>(onError(error, stack)); - } - }); - - /// Extract a value `A` given the current dependency `E`. - factory ReaderTaskEither.asks(R Function(E) f) => ReaderTaskEither( - (env) async => right(f(env)), - ); - - /// Read the current dependency `E`. - static ReaderTaskEither<E, L, E> ask<E, L>() => ReaderTaskEither( - (env) async => right(env), - ); -} diff --git a/packages/fpdart/lib/src/state.dart b/packages/fpdart/lib/src/state.dart deleted file mode 100644 index 0107597..0000000 --- a/packages/fpdart/lib/src/state.dart +++ /dev/null @@ -1,159 +0,0 @@ -import 'function.dart'; -import 'state_async.dart'; -import 'typeclass/typeclass.export.dart'; -import 'unit.dart'; - -/// Tag the [HKT2] interface for the actual [State]. -abstract final class _StateHKT {} - -/// `State<S, A>` is used to store, update, and extract state in a functional way. -/// -/// `S` is a State (e.g. the current _State_ of your Bank Account). -/// `A` is value that you _extract out of the [State]_ -/// (Account Balance fetched from the current state of your Bank Account `S`). -final class State<S, A> extends HKT2<_StateHKT, S, A> - with - Functor2<_StateHKT, S, A>, - Applicative2<_StateHKT, S, A>, - Monad2<_StateHKT, S, A> { - final (A, S) Function(S state) _run; - - /// Build a new [State] given a `(A, S) Function(S)`. - const State(this._run); - - /// Flat a [State] contained inside another [State] to be a single [State]. - factory State.flatten(State<S, State<S, A>> state) => state.flatMap(identity); - - /// Lift a sync [State] to an async [StateAsync]. - StateAsync<S, A> toStateAsync() => StateAsync.fromState(this); - - /// Used to chain multiple functions that return a [State]. - @override - State<S, C> flatMap<C>(covariant State<S, C> Function(A a) f) => - State((state) { - final tuple = run(state); - return f(tuple.$1).run(tuple.$2); - }); - - /// Apply the function contained inside `a` to change the value of type `A` to - /// a value of type `C`. - @override - State<S, C> ap<C>(covariant State<S, C Function(A a)> a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - /// Return a `State<S, C>` containing `c` as value. - @override - State<S, C> pure<C>(C c) => State((state) => (c, state)); - - /// Change the value inside `State<S, A>` from type `A` to type `C` using `f`. - @override - State<S, C> map<C>(C Function(A a) f) => ap(pure(f)); - - /// Change type of this [State] based on its value of type `A` and the - /// value of type `C` of another [State]. - @override - State<S, D> map2<C, D>(covariant State<S, C> m1, D Function(A a, C c) f) => - flatMap((a) => m1.map((c) => f(a, c))); - - /// Change type of this [State] based on its value of type `A`, the - /// value of type `C` of a second [State], and the value of type `D` - /// of a third [State]. - @override - State<S, E> map3<C, D, E>(covariant State<S, C> m1, covariant State<S, D> m2, - E Function(A a, C c, D d) f) => - flatMap((a) => m1.flatMap((c) => m2.map((d) => f(a, c, d)))); - - /// Chain the result of `then` to this [State]. - @override - State<S, C> andThen<C>(covariant State<S, C> Function() then) => - flatMap((_) => then()); - - /// Chain multiple functions having the same state `S`. - @override - State<S, C> call<C>(covariant State<S, C> state) => flatMap((_) => state); - - /// Extract the current state `S`. - State<S, S> get() => State((state) => (state, state)); - - /// Change the value getter based on the current state `S`. - State<S, A> gets(A Function(S state) f) => - State((state) => (f(state), state)); - - /// Change the current state `S` using `f` and return nothing ([Unit]). - State<S, Unit> modify(S Function(S state) f) => - State((state) => (unit, f(state))); - - /// Set a new state and return nothing ([Unit]). - State<S, Unit> put(S state) => State((_) => (unit, state)); - - /// Chain a request that returns another [State], execute it, ignore - /// the result, and return the same value as the current [State]. - /// - /// **Note**: `chainFirst` will not change the value of `first` for the state, - /// but **it will change the value of `second`** when calling `run()`. - @override - State<S, A> chainFirst<C>( - covariant State<S, C> Function(A a) chain, - ) => - flatMap((a) => chain(a).map((_) => a)); - - /// Execute `run` and extract the value `A`. - /// - /// To extract both the value and the state use `run`. - /// - /// To extract only the state `S` use `execute`. - A evaluate(S state) => run(state).$1; - - /// Execute `run` and extract the state `S`. - /// - /// To extract both the value and the state use `run`. - /// - /// To extract only the value `A` use `evaluate`. - S execute(S state) => run(state).$2; - - /// Extract value `A` and state `S` by passing the original state `S`. - /// - /// To extract only the value `A` use `evaluate`. - /// - /// To extract only the state `S` use `execute`. - (A, S) run(S state) => _run(state); - - /// {@template fpdart_traverse_list_state} - /// Map each element in the list to a [State] using the function `f`, - /// and collect the result in a `State<S, List<B>>`. - /// {@endtemplate} - /// - /// Same as `State.traverseList` but passing `index` in the map function. - static State<S, List<B>> traverseListWithIndex<S, A, B>( - List<A> list, State<S, B> Function(A a, int i) f) { - return State((state) { - final resultList = <B>[]; - var out = state; - for (var i = 0; i < list.length; i++) { - final (b, s) = f(list[i], i).run(out); - resultList.add(b); - out = s; - } - return (resultList, out); - }); - } - - /// {@macro fpdart_traverse_list_state} - /// - /// Same as `State.traverseListWithIndex` but without `index` in the map function. - static State<S, List<B>> traverseList<S, A, B>( - List<A> list, State<S, B> Function(A a) f) => - traverseListWithIndex<S, A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_state} - /// Convert a `List<State<S, A>>` to a single `State<S, List<A>>`. - /// {@endtemplate} - static State<S, List<A>> sequenceList<S, A>(List<State<S, A>> list) => - traverseList(list, identity); - - @override - bool operator ==(Object other) => (other is State) && other._run == _run; - - @override - int get hashCode => _run.hashCode; -} diff --git a/packages/fpdart/lib/src/state_async.dart b/packages/fpdart/lib/src/state_async.dart deleted file mode 100644 index 8409ee7..0000000 --- a/packages/fpdart/lib/src/state_async.dart +++ /dev/null @@ -1,132 +0,0 @@ -import 'function.dart'; -import 'state.dart'; -import 'typeclass/typeclass.export.dart'; -import 'unit.dart'; - -/// Tag the [HKT2] interface for the actual [StateAsync]. -abstract final class _StateAsyncHKT {} - -/// `StateAsync<S, A>` is used to store, update, and extract async state in a functional way. -/// -/// `S` is a State (e.g. the current _State_ of your Bank Account). -/// `A` is value that you _extract out of the [StateAsync]_ -/// (Account Balance fetched from the current state of your Bank Account `S`). -/// -/// Used when fetching and updating the state is **asynchronous**. Use [State] otherwise. -final class StateAsync<S, A> extends HKT2<_StateAsyncHKT, S, A> - with - Functor2<_StateAsyncHKT, S, A>, - Applicative2<_StateAsyncHKT, S, A>, - Monad2<_StateAsyncHKT, S, A> { - final Future<(A, S)> Function(S state) _run; - - /// Build a new [StateAsync] given a `Future<(A, S)> Function(S)`. - const StateAsync(this._run); - - /// Flat a [StateAsync] contained inside another [StateAsync] to be a single [StateAsync]. - factory StateAsync.flatten(StateAsync<S, StateAsync<S, A>> state) => - state.flatMap(identity); - - /// Build a new [StateAsync] by lifting a sync [State] to async. - factory StateAsync.fromState(State<S, A> state) => - StateAsync((s) async => state.run(s)); - - /// Used to chain multiple functions that return a [StateAsync]. - @override - StateAsync<S, C> flatMap<C>(covariant StateAsync<S, C> Function(A a) f) => - StateAsync((state) async { - final tuple = await run(state); - return f(tuple.$1).run(tuple.$2); - }); - - /// Apply the function contained inside `a` to change the value of type `A` to - /// a value of type `C`. - @override - StateAsync<S, C> ap<C>(covariant StateAsync<S, C Function(A a)> a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - /// Return a `StateAsync<S, C>` containing `c` as value. - @override - StateAsync<S, C> pure<C>(C c) => StateAsync((state) async => (c, state)); - - /// Change the value inside `StateAsync<S, A>` from type `A` to type `C` using `f`. - @override - StateAsync<S, C> map<C>(C Function(A a) f) => ap(pure(f)); - - /// Change type of this [StateAsync] based on its value of type `A` and the - /// value of type `C` of another [StateAsync]. - @override - StateAsync<S, D> map2<C, D>( - covariant StateAsync<S, C> m1, D Function(A a, C c) f) => - flatMap((a) => m1.map((c) => f(a, c))); - - /// Change type of this [StateAsync] based on its value of type `A`, the - /// value of type `C` of a second [StateAsync], and the value of type `D` - /// of a third [StateAsync]. - @override - StateAsync<S, E> map3<C, D, E>(covariant StateAsync<S, C> m1, - covariant StateAsync<S, D> m2, E Function(A a, C c, D d) f) => - flatMap((a) => m1.flatMap((c) => m2.map((d) => f(a, c, d)))); - - /// Chain the result of `then` to this [StateAsync]. - @override - StateAsync<S, C> andThen<C>(covariant StateAsync<S, C> Function() then) => - flatMap((_) => then()); - - /// Chain multiple functions having the same state `S`. - @override - StateAsync<S, C> call<C>(covariant StateAsync<S, C> state) => - flatMap((_) => state); - - /// Extract the current state `S`. - StateAsync<S, S> get() => StateAsync((state) async => (state, state)); - - /// Change the value getter based on the current state `S`. - StateAsync<S, A> gets(A Function(S state) f) => - StateAsync((state) async => (f(state), state)); - - /// Change the current state `S` using `f` and return nothing ([Unit]). - StateAsync<S, Unit> modify(S Function(S state) f) => - StateAsync((state) async => (unit, f(state))); - - /// Set a new state and return nothing ([Unit]). - StateAsync<S, Unit> put(S state) => StateAsync((_) async => (unit, state)); - - /// Execute `run` and extract the value `A`. - /// - /// To extract both the value and the state use `run`. - /// - /// To extract only the state `S` use `execute`. - Future<A> evaluate(S state) async => (await run(state)).$1; - - /// Execute `run` and extract the state `S`. - /// - /// To extract both the value and the state use `run`. - /// - /// To extract only the value `A` use `evaluate`. - Future<S> execute(S state) async => (await run(state)).$2; - - /// Chain a request that returns another [StateAsync], execute it, ignore - /// the result, and return the same value as the current [StateAsync]. - /// - /// **Note**: `chainFirst` will not change the value of `first` for the state, - /// but **it will change the value of `second`** when calling `run()`. - @override - StateAsync<S, A> chainFirst<C>( - covariant StateAsync<S, C> Function(A a) chain, - ) => - flatMap((a) => chain(a).map((_) => a)); - - /// Extract value `A` and state `S` by passing the original state `S`. - /// - /// To extract only the value `A` use `evaluate`. - /// - /// To extract only the state `S` use `execute`. - Future<(A, S)> run(S state) => _run(state); - - @override - bool operator ==(Object other) => (other is StateAsync) && other._run == _run; - - @override - int get hashCode => _run.hashCode; -} diff --git a/packages/fpdart/lib/src/task.dart b/packages/fpdart/lib/src/task.dart deleted file mode 100644 index 12dce59..0000000 --- a/packages/fpdart/lib/src/task.dart +++ /dev/null @@ -1,190 +0,0 @@ -import 'either.dart'; -import 'extension/iterable_extension.dart'; -import 'function.dart'; -import 'option.dart'; -import 'task_either.dart'; -import 'task_option.dart'; -import 'typeclass/applicative.dart'; -import 'typeclass/functor.dart'; -import 'typeclass/hkt.dart'; -import 'typeclass/monad.dart'; - -typedef DoAdapterTask = Future<A> Function<A>(Task<A>); -Future<A> _doAdapter<A>(Task<A> task) => task.run(); - -typedef DoFunctionTask<A> = Future<A> Function(DoAdapterTask $); - -/// Tag the [HKT] interface for the actual [Task]. -abstract final class _TaskHKT {} - -/// [Task] represents an asynchronous computation that yields a value of type `A` and **never fails**. -/// -/// If you want to represent an asynchronous computation that may fail, see [TaskEither]. -final class Task<A> extends HKT<_TaskHKT, A> - with Functor<_TaskHKT, A>, Applicative<_TaskHKT, A>, Monad<_TaskHKT, A> { - final Future<A> Function() _run; - - /// Build a [Task] from a function returning a [Future]. - const Task(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory Task.Do(DoFunctionTask<A> f) => Task(() => f(_doAdapter)); - - /// Build a [Task] that returns `a`. - factory Task.of(A a) => Task<A>(() async => a); - - /// Flat a [Task] contained inside another [Task] to be a single [Task]. - factory Task.flatten(Task<Task<A>> task) => task.flatMap(identity); - - /// Apply the function contained inside `a` to change the value of type `A` to - /// a value of type `B`. - @override - Task<B> ap<B>(covariant Task<B Function(A a)> a) => - Task(() => a.run().then((f) => run().then((v) => f(v)))); - - /// Used to chain multiple functions that return a [Task]. - /// - /// You can extract the value inside the [Task] without actually running it. - @override - Task<B> flatMap<B>(covariant Task<B> Function(A a) f) => - Task(() => run().then((v) => f(v).run())); - - /// Return a [Task] returning the value `b`. - @override - Task<B> pure<B>(B a) => Task(() async => a); - - /// Change the returning value of the [Task] from type - /// `A` to type `B` using `f`. - @override - Task<B> map<B>(B Function(A a) f) => ap(pure(f)); - - /// Change type of this [Task] based on its value of type `A` and the - /// value of type `C` of another [Task]. - @override - Task<D> map2<C, D>(covariant Task<C> mc, D Function(A a, C c) f) => - flatMap((a) => mc.map((c) => f(a, c))); - - /// Change type of this [Task] based on its value of type `A`, the - /// value of type `C` of a second [Task], and the value of type `D` - /// of a third [Task]. - @override - Task<E> map3<C, D, E>(covariant Task<C> mc, covariant Task<D> md, - E Function(A a, C c, D d) f) => - flatMap((a) => mc.flatMap((c) => md.map((d) => f(a, c, d)))); - - /// Run this [Task] and right after the [Task] returned from `then`. - @override - Task<B> andThen<B>(covariant Task<B> Function() then) => - flatMap((_) => then()); - - @override - Task<A> chainFirst<B>(covariant Task<B> Function(A a) chain) => - flatMap((a) => chain(a).map((b) => a)); - - /// Chain multiple [Task] functions. - @override - Task<B> call<B>(covariant Task<B> chain) => flatMap((_) => chain); - - /// Creates a task that will complete after a time delay specified by a [Duration]. - Task<A> delay(Duration duration) => Task(() => Future.delayed(duration, run)); - - /// Run the task and return a [Future]. - Future<A> run() => _run(); - - /// Convert this [Task] to [TaskOption]. - TaskOption<A> toTaskOption() => - TaskOption(() async => Option.of(await run())); - - /// Convert this [Task] to [TaskEither]. - TaskEither<L, A> toTaskEither<L>() => - TaskEither<L, A>(() async => Either.of(await run())); - - /// {@template fpdart_traverse_list_task} - /// Map each element in the list to a [Task] using the function `f`, - /// and collect the result in an `Task<List<B>>`. - /// - /// Each [Task] is executed in parallel. This strategy is faster than - /// sequence, but **the order of the request is not guaranteed**. - /// - /// If you need [Task] to be executed in sequence, use `traverseListWithIndexSeq`. - /// {@endtemplate} - /// - /// Same as `Task.traverseList` but passing `index` in the map function. - static Task<List<B>> traverseListWithIndex<A, B>( - List<A> list, - Task<B> Function(A a, int i) f, - ) => - Task<List<B>>( - () => Future.wait<B>( - list.mapWithIndex( - (a, i) => f(a, i).run(), - ), - ), - ); - - /// {@macro fpdart_traverse_list_task} - /// - /// Same as `Task.traverseListWithIndex` but without `index` in the map function. - static Task<List<B>> traverseList<A, B>( - List<A> list, - Task<B> Function(A a) f, - ) => - traverseListWithIndex<A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_traverse_list_seq_task} - /// Map each element in the list to a [Task] using the function `f`, - /// and collect the result in an `Task<List<B>>`. - /// - /// Each [Task] is executed in sequence. This strategy **takes more time than - /// parallel**, but it ensures that all the request are executed in order. - /// - /// If you need [Task] to be executed in parallel, use `traverseListWithIndex`. - /// {@endtemplate} - /// - /// Same as `Task.traverseListSeq` but passing `index` in the map function. - static Task<List<B>> traverseListWithIndexSeq<A, B>( - List<A> list, - Task<B> Function(A a, int i) f, - ) => - Task<List<B>>(() async { - List<B> collect = []; - for (var i = 0; i < list.length; i++) { - collect.add(await f(list[i], i).run()); - } - return collect; - }); - - /// {@macro fpdart_traverse_list_seq_task} - /// - /// Same as `Task.traverseListWithIndexSeq` but without `index` in the map function. - static Task<List<B>> traverseListSeq<A, B>( - List<A> list, - Task<B> Function(A a) f, - ) => - traverseListWithIndexSeq<A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_task} - /// Convert a `List<Task<A>>` to a single `Task<List<A>>`. - /// - /// Each [Task] will be executed in parallel. - /// - /// If you need [Task] to be executed in sequence, use `sequenceListSeq`. - /// {@endtemplate} - static Task<List<A>> sequenceList<A>( - List<Task<A>> list, - ) => - traverseList(list, identity); - - /// {@template fpdart_sequence_list_seq_task} - /// Convert a `List<Task<A>>` to a single `Task<List<A>>`. - /// - /// Each [Task] will be executed in sequence. - /// - /// If you need [Task] to be executed in parallel, use `sequenceList`. - /// {@endtemplate} - static Task<List<A>> sequenceListSeq<A>( - List<Task<A>> list, - ) => - traverseListSeq(list, identity); -} diff --git a/packages/fpdart/lib/src/task_either.dart b/packages/fpdart/lib/src/task_either.dart deleted file mode 100644 index 99793cf..0000000 --- a/packages/fpdart/lib/src/task_either.dart +++ /dev/null @@ -1,387 +0,0 @@ -import 'either.dart'; -import 'function.dart'; -import 'option.dart'; -import 'task.dart'; -import 'typeclass/alt.dart'; -import 'typeclass/applicative.dart'; -import 'typeclass/functor.dart'; -import 'typeclass/hkt.dart'; -import 'typeclass/monad.dart'; - -final class _TaskEitherThrow<L> { - final L value; - const _TaskEitherThrow(this.value); -} - -typedef DoAdapterTaskEither<E> = Future<A> Function<A>(TaskEither<E, A>); -DoAdapterTaskEither<L> _doAdapter<L>() => - <A>(taskEither) => taskEither.run().then( - (either) => either.getOrElse((l) => throw _TaskEitherThrow(l)), - ); - -typedef DoFunctionTaskEither<L, A> = Future<A> Function( - DoAdapterTaskEither<L> $); - -/// Tag the [HKT2] interface for the actual [TaskEither]. -abstract final class _TaskEitherHKT {} - -/// `TaskEither<L, R>` represents an asynchronous computation that -/// either yields a value of type `R` or fails yielding an error of type `L`. -/// -/// If you want to represent an asynchronous computation that never fails, see [Task]. -final class TaskEither<L, R> extends HKT2<_TaskEitherHKT, L, R> - with - Functor2<_TaskEitherHKT, L, R>, - Applicative2<_TaskEitherHKT, L, R>, - Monad2<_TaskEitherHKT, L, R>, - Alt2<_TaskEitherHKT, L, R> { - final Future<Either<L, R>> Function() _run; - - /// Build a [TaskEither] from a function returning a `Future<Either<L, R>>`. - const TaskEither(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory TaskEither.Do(DoFunctionTaskEither<L, R> f) => TaskEither(() async { - try { - return Either.of(await f(_doAdapter<L>())); - } on _TaskEitherThrow<L> catch (e) { - return Either.left(e.value); - } - }); - - /// Used to chain multiple functions that return a [TaskEither]. - /// - /// You can extract the value of every [Right] in the chain without - /// handling all possible missing cases. - /// If running any of the tasks in the chain returns [Left], the result is [Left]. - @override - TaskEither<L, C> flatMap<C>(covariant TaskEither<L, C> Function(R r) f) => - TaskEither(() => run().then( - (either) async => either.match( - left, - (r) => f(r).run(), - ), - )); - - /// Chain an [Either] to [TaskEither] by converting it from sync to async. - TaskEither<L, C> bindEither<C>(Either<L, C> either) => - flatMap((_) => either.toTaskEither()); - - /// Chain a function that takes the current value `R` inside this [TaskEither] - /// and returns [Either]. - /// - /// Similar to `flatMap`, but `f` returns [Either] instead of [TaskEither]. - TaskEither<L, C> chainEither<C>(Either<L, C> Function(R r) f) => flatMap( - (r) => f(r).toTaskEither(), - ); - - /// Returns a [TaskEither] that returns a `Right(a)`. - @override - TaskEither<L, C> pure<C>(C a) => TaskEither(() async => Right(a)); - - /// Change the return type of this [TaskEither] based on its value of type `R` and the - /// value of type `C` of another [TaskEither]. - @override - TaskEither<L, D> map2<C, D>( - covariant TaskEither<L, C> m1, D Function(R b, C c) f) => - flatMap((b) => m1.map((c) => f(b, c))); - - /// Change the return type of this [TaskEither] based on its value of type `R`, the - /// value of type `C` of a second [TaskEither], and the value of type `D` - /// of a third [TaskEither]. - @override - TaskEither<L, E> map3<C, D, E>(covariant TaskEither<L, C> m1, - covariant TaskEither<L, D> m2, E Function(R b, C c, D d) f) => - flatMap((b) => m1.flatMap((c) => m2.map((d) => f(b, c, d)))); - - /// If running this [TaskEither] returns [Right], then return the result of calling `then`. - /// Otherwise return [Left]. - @override - TaskEither<L, C> andThen<C>(covariant TaskEither<L, C> Function() then) => - flatMap((_) => then()); - - /// If running this [TaskEither] returns [Right], then change its value from type `R` to - /// type `C` using function `f`. - @override - TaskEither<L, C> map<C>(C Function(R r) f) => ap(pure(f)); - - /// Change the value in the [Left] of [TaskEither]. - TaskEither<C, R> mapLeft<C>(C Function(L l) f) => TaskEither( - () async => (await run()).match((l) => Either.left(f(l)), Either.of), - ); - - /// Define two functions to change both the [Left] and [Right] value of the - /// [TaskEither]. - /// - /// {@macro fpdart_bimap_either} - TaskEither<C, D> bimap<C, D>(C Function(L l) mLeft, D Function(R r) mRight) => - mapLeft(mLeft).map(mRight); - - /// Apply the function contained inside `a` to change the value on the [Right] from - /// type `R` to a value of type `C`. - @override - TaskEither<L, C> ap<C>(covariant TaskEither<L, C Function(R r)> a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - /// Chain multiple functions having the same left type `L`. - @override - TaskEither<L, C> call<C>(covariant TaskEither<L, C> chain) => - flatMap((_) => chain); - - /// Change this [TaskEither] from `TaskEither<L, R>` to `TaskEither<R, L>`. - TaskEither<R, L> swap() => - TaskEither(() async => (await run()).match(right, left)); - - /// When this [TaskEither] returns [Right], then return the current [TaskEither]. - /// Otherwise return the result of `orElse`. - /// - /// Used to provide an **alt**ernative [TaskEither] in case the current one returns [Left]. - @override - TaskEither<L, R> alt(covariant TaskEither<L, R> Function() orElse) => - TaskEither(() async => (await run()).match((_) => orElse().run(), right)); - - /// If `f` applied on this [TaskEither] as [Right] returns `true`, then return this [TaskEither]. - /// If it returns `false`, return the result of `onFalse` in a [Left]. - TaskEither<L, R> filterOrElse( - bool Function(R r) f, L Function(R r) onFalse) => - flatMap((r) => f(r) ? TaskEither.of(r) : TaskEither.left(onFalse(r))); - - /// When this [TaskEither] returns a [Left] then return the result of `orElse`. - /// Otherwise return this [TaskEither]. - TaskEither<TL, R> orElse<TL>(TaskEither<TL, R> Function(L l) orElse) => - TaskEither(() async => (await run()).match( - (l) => orElse(l).run(), (r) => TaskEither<TL, R>.right(r).run())); - - /// Convert this [TaskEither] to a [Task]. - /// - /// The task returns a [Right] when [TaskEither] returns [Right]. - /// Otherwise map the type `L` of [TaskEither] to type `R` by calling `orElse`. - Task<R> getOrElse(R Function(L l) orElse) => - Task(() async => (await run()).match(orElse, identity)); - - /// Pattern matching to convert a [TaskEither] to a [Task]. - /// - /// Execute `onLeft` when running this [TaskEither] returns a [Left]. - /// Otherwise execute `onRight`. - Task<A> match<A>(A Function(L l) onLeft, A Function(R r) onRight) => - Task(() async => (await run()).match(onLeft, onRight)); - - /// Creates a [TaskEither] that will complete after a time delay specified by a [Duration]. - TaskEither<L, R> delay(Duration duration) => - TaskEither(() => Future.delayed(duration, run)); - - /// Chain a request that returns another [TaskEither], execute it, ignore - /// the result, and return the same value as the current [TaskEither]. - @override - TaskEither<L, R> chainFirst<C>( - covariant TaskEither<L, C> Function(R b) chain, - ) => - flatMap((b) => chain(b).map((c) => b).orElse((l) => TaskEither.right(b))); - - /// Run the task and return a `Future<Either<L, R>>`. - Future<Either<L, R>> run() => _run(); - - /// Build a [TaskEither] that returns a `Right(r)`. - /// - /// Same of `TaskEither.right`. - factory TaskEither.of(R r) => TaskEither(() async => Either.of(r)); - - /// Flat a [TaskEither] contained inside another [TaskEither] to be a single [TaskEither]. - factory TaskEither.flatten(TaskEither<L, TaskEither<L, R>> taskEither) => - taskEither.flatMap(identity); - - /// Build a [TaskEither] that returns a `Right(right)`. - /// - /// Same of `TaskEither.of`. - factory TaskEither.right(R right) => TaskEither(() async => Either.of(right)); - - /// Build a [TaskEither] that returns a `Left(left)`. - factory TaskEither.left(L left) => TaskEither(() async => Left(left)); - - /// Build a [TaskEither] that returns a [Left] containing the result of running `task`. - factory TaskEither.leftTask(Task<L> task) => - TaskEither(() => task.run().then(left)); - - /// Build a [TaskEither] that returns a [Right] containing the result of running `task`. - /// - /// Same of `TaskEither.fromTask` - factory TaskEither.rightTask(Task<R> task) => - TaskEither(() async => Right(await task.run())); - - /// Build a [TaskEither] from the result of running `task`. - /// - /// Same of `TaskEither.rightTask` - factory TaskEither.fromTask(Task<R> task) => - TaskEither(() async => Right(await task.run())); - - /// {@template fpdart_from_nullable_task_either} - /// If `r` is `null`, then return the result of `onNull` in [Left]. - /// Otherwise return `Right(r)`. - - /// {@endtemplate} - factory TaskEither.fromNullable(R? r, L Function() onNull) => - Either.fromNullable(r, onNull).toTaskEither(); - - /// {@macro fpdart_from_nullable_task_either} - factory TaskEither.fromNullableAsync(R? r, Task<L> onNull) => TaskEither( - () async => r != null ? Either.of(r) : Either.left(await onNull.run())); - - /// When calling `predicate` with `value` returns `true`, then running [TaskEither] returns `Right(value)`. - /// Otherwise return `onFalse`. - factory TaskEither.fromPredicate( - R value, bool Function(R a) predicate, L Function(R a) onFalse) => - TaskEither( - () async => predicate(value) ? Right(value) : Left(onFalse(value))); - - /// Build a [TaskEither] from `option`. - /// - /// When `option` is [Some], then return [Right] when - /// running [TaskEither]. Otherwise return `onNone`. - factory TaskEither.fromOption(Option<R> option, L Function() onNone) => - TaskEither(() async => option.match( - () => Left(onNone()), - Right.new, - )); - - /// Build a [TaskEither] that returns `either`. - factory TaskEither.fromEither(Either<L, R> either) => - TaskEither(() async => either); - - /// {@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<int> imperative(String str) async { - /// try { - /// return int.parse(str); - /// } catch (e) { - /// return -1; /// What does -1 means? 🤨 - /// } - /// } - /// - /// TaskEither<String, int> functional(String str) { - /// return TaskEither.tryCatch( - /// () async => int.parse(str), - /// /// Clear error 🪄 - /// (error, stackTrace) => "Parsing error: $error", - /// ); - /// } - /// ``` - factory TaskEither.tryCatch(Future<R> Function() run, - L Function(Object error, StackTrace stackTrace) onError) => - TaskEither<L, R>(() async { - try { - return Right<L, R>(await run()); - } catch (error, stack) { - return Left<L, R>(onError(error, stack)); - } - }); - - /// {@template fpdart_traverse_list_task_either} - /// Map each element in the list to a [TaskEither] using the function `f`, - /// and collect the result in an `TaskEither<E, List<B>>`. - /// - /// Each [TaskEither] is executed in parallel. This strategy is faster than - /// sequence, but **the order of the request is not guaranteed**. - /// - /// If you need [TaskEither] to be executed in sequence, use `traverseListWithIndexSeq`. - /// {@endtemplate} - /// - /// Same as `TaskEither.traverseList` but passing `index` in the map function. - static TaskEither<E, List<B>> traverseListWithIndex<E, A, B>( - List<A> list, - TaskEither<E, B> Function(A a, int i) f, - ) => - TaskEither<E, List<B>>( - () async => Either.sequenceList( - await Task.traverseListWithIndex<A, Either<E, B>>( - list, - (a, i) => Task(() => f(a, i).run()), - ).run(), - ), - ); - - /// {@macro fpdart_traverse_list_task_either} - /// - /// Same as `TaskEither.traverseListWithIndex` but without `index` in the map function. - static TaskEither<E, List<B>> traverseList<E, A, B>( - List<A> list, - TaskEither<E, B> Function(A a) f, - ) => - traverseListWithIndex<E, A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_task_either} - /// Convert a `List<TaskEither<E, A>>` to a single `TaskEither<E, List<A>>`. - /// - /// Each [TaskEither] will be executed in parallel. - /// - /// If you need [TaskEither] to be executed in sequence, use `sequenceListSeq`. - /// {@endtemplate} - static TaskEither<E, List<A>> sequenceList<E, A>( - List<TaskEither<E, A>> list, - ) => - traverseList(list, identity); - - /// {@template fpdart_traverse_list_seq_task_either} - /// Map each element in the list to a [TaskEither] using the function `f`, - /// and collect the result in an `TaskEither<E, List<B>>`. - /// - /// Each [TaskEither] is executed in sequence. This strategy **takes more time than - /// parallel**, but it ensures that all the request are executed in order. - /// - /// If you need [TaskEither] to be executed in parallel, use `traverseListWithIndex`. - /// {@endtemplate} - /// - /// Same as `TaskEither.traverseList` but passing `index` in the map function. - static TaskEither<E, List<B>> traverseListWithIndexSeq<E, A, B>( - List<A> list, - TaskEither<E, B> Function(A a, int i) f, - ) => - TaskEither<E, List<B>>( - () async => Either.sequenceList( - await Task.traverseListWithIndexSeq<A, Either<E, B>>( - list, - (a, i) => Task(() => f(a, i).run()), - ).run(), - ), - ); - - /// {@macro fpdart_traverse_list_seq_task_either} - /// - /// Same as `TaskEither.traverseListWithIndex` but without `index` in the map function. - static TaskEither<E, List<B>> traverseListSeq<E, A, B>( - List<A> list, - TaskEither<E, B> Function(A a) f, - ) => - traverseListWithIndexSeq<E, A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_seq_task_either} - /// Convert a `List<TaskEither<E, A>>` to a single `TaskEither<E, List<A>>`. - /// - /// Each [TaskEither] will be executed in sequence. - /// - /// If you need [TaskEither] to be executed in parallel, use `sequenceList`. - /// {@endtemplate} - static TaskEither<E, List<A>> sequenceListSeq<E, A>( - List<TaskEither<E, A>> list, - ) => - traverseListSeq(list, identity); - - /// {@macro fpdart_try_catch_task_either} - /// - /// It wraps the `TaskEither.tryCatch` factory to make chaining with `flatMap` - /// easier. - static TaskEither<L, R> Function(T a) tryCatchK<L, R, T>( - Future<R> Function(T a) run, - L Function(Object error, StackTrace stackTrace) onError) => - (a) => TaskEither.tryCatch( - () => run(a), - onError, - ); -} diff --git a/packages/fpdart/lib/src/task_option.dart b/packages/fpdart/lib/src/task_option.dart deleted file mode 100644 index b5b31fb..0000000 --- a/packages/fpdart/lib/src/task_option.dart +++ /dev/null @@ -1,313 +0,0 @@ -import 'either.dart'; -import 'extension/option_extension.dart'; -import 'function.dart'; -import 'option.dart'; -import 'task.dart'; -import 'task_either.dart'; -import 'typeclass/alt.dart'; -import 'typeclass/applicative.dart'; -import 'typeclass/functor.dart'; -import 'typeclass/hkt.dart'; -import 'typeclass/monad.dart'; - -final class _TaskOptionThrow { - const _TaskOptionThrow(); -} - -typedef DoAdapterTaskOption = Future<A> Function<A>(TaskOption<A>); -Future<A> _doAdapter<A>(TaskOption<A> taskOption) => taskOption.run().then( - (option) => option.getOrElse(() => throw const _TaskOptionThrow()), - ); - -typedef DoFunctionTaskOption<A> = Future<A> Function(DoAdapterTaskOption $); - -/// Tag the [HKT] interface for the actual [TaskOption]. -abstract final class _TaskOptionHKT {} - -/// `TaskOption<R>` represents an **asynchronous** computation that -/// may fails yielding a [None] or returns a `Some(R)` when successful. -/// -/// If you want to represent an asynchronous computation that never fails, see [Task]. -/// -/// If you want to represent an asynchronous computation that returns an object when it fails, -/// see [TaskEither]. -final class TaskOption<R> extends HKT<_TaskOptionHKT, R> - with - Functor<_TaskOptionHKT, R>, - Applicative<_TaskOptionHKT, R>, - Monad<_TaskOptionHKT, R>, - Alt<_TaskOptionHKT, R> { - final Future<Option<R>> Function() _run; - - /// Build a [TaskOption] from a function returning a `Future<Option<R>>`. - const TaskOption(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory TaskOption.Do(DoFunctionTaskOption<R> f) => TaskOption(() async { - try { - return Option.of(await f(_doAdapter)); - } on _TaskOptionThrow catch (_) { - return const Option.none(); - } - }); - - /// Used to chain multiple functions that return a [TaskOption]. - /// - /// You can extract the value of every [Some] in the chain without - /// handling all possible missing cases. - /// If running any of the tasks in the chain returns [None], the result is [None]. - @override - TaskOption<C> flatMap<C>(covariant TaskOption<C> Function(R r) f) => - TaskOption(() => run().then( - (option) async => option.match( - Option.none, - (r) => f(r).run(), - ), - )); - - /// Returns a [TaskOption] that returns `Some(c)`. - @override - TaskOption<C> pure<C>(C c) => TaskOption(() async => Option.of(c)); - - /// Change the return type of this [TaskOption] based on its value of type `R` and the - /// value of type `C` of another [TaskOption]. - @override - TaskOption<D> map2<C, D>( - covariant TaskOption<C> m1, D Function(R b, C c) f) => - flatMap((b) => m1.map((c) => f(b, c))); - - /// Change the return type of this [TaskOption] based on its value of type `R`, the - /// value of type `C` of a second [TaskOption], and the value of type `D` - /// of a third [TaskOption]. - @override - TaskOption<E> map3<C, D, E>(covariant TaskOption<C> m1, - covariant TaskOption<D> m2, E Function(R b, C c, D d) f) => - flatMap((b) => m1.flatMap((c) => m2.map((d) => f(b, c, d)))); - - /// If running this [TaskOption] returns [Some], then return the result of calling `then`. - /// Otherwise return [None]. - @override - TaskOption<C> andThen<C>(covariant TaskOption<C> Function() then) => - flatMap((_) => then()); - - /// Chain multiple [TaskOption] functions. - @override - TaskOption<B> call<B>(covariant TaskOption<B> chain) => flatMap((_) => chain); - - /// If running this [TaskOption] returns [Some], then change its value from type `R` to - /// type `C` using function `f`. - @override - TaskOption<C> map<C>(C Function(R r) f) => ap(pure(f)); - - /// Apply the function contained inside `a` to change the value on the [Some] from - /// type `R` to a value of type `C`. - @override - TaskOption<C> ap<C>(covariant TaskOption<C Function(R r)> a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - /// When this [TaskOption] returns [Some], then return the current [TaskOption]. - /// Otherwise return the result of `orElse`. - /// - /// Used to provide an **alt**ernative [TaskOption] in case the current one returns [None]. - @override - TaskOption<R> alt(covariant TaskOption<R> Function() orElse) => - TaskOption(() async => (await run()).match( - () => orElse().run(), - some, - )); - - /// When this [TaskOption] returns a [None] then return the result of `orElse`. - /// Otherwise return this [TaskOption]. - TaskOption<R> orElse<TL>(TaskOption<R> Function() orElse) => - TaskOption(() async => (await run()).match( - () => orElse().run(), - (r) => TaskOption<R>.some(r).run(), - )); - - /// Convert this [TaskOption] to a [Task]. - /// - /// The task returns a [Some] when [TaskOption] returns [Some]. - /// Otherwise map the type `L` of [TaskOption] to type `R` by calling `orElse`. - Task<R> getOrElse(R Function() orElse) => - Task(() async => (await run()).match( - orElse, - identity, - )); - - /// Pattern matching to convert a [TaskOption] to a [Task]. - /// - /// Execute `onNone` when running this [TaskOption] returns a [None]. - /// Otherwise execute `onSome`. - Task<A> match<A>(A Function() onNone, A Function(R r) onSome) => - Task(() async => (await run()).match( - onNone, - onSome, - )); - - /// Creates a [TaskOption] that will complete after a time delay specified by a [Duration]. - TaskOption<R> delay(Duration duration) => - TaskOption(() => Future.delayed(duration, run)); - - /// Run the task and return a `Future<Option<R>>`. - Future<Option<R>> run() => _run(); - - /// Convert this [TaskOption] to [TaskEither]. - /// - /// If the value inside [TaskOption] is [None], then use `onNone` to convert it - /// to a value of type `L`. - TaskEither<L, R> toTaskEither<L>(L Function() onNone) => - TaskEither(() async => Either.fromOption(await run(), onNone)); - - /// Build a [TaskOption] that returns a `Some(r)`. - /// - /// Same of `TaskOption.some`. - factory TaskOption.of(R r) => TaskOption(() async => Option.of(r)); - - /// Flat a [TaskOption] contained inside another [TaskOption] to be a single [TaskOption]. - factory TaskOption.flatten(TaskOption<TaskOption<R>> taskOption) => - taskOption.flatMap(identity); - - /// Build a [TaskOption] that returns a `Some(r)`. - /// - /// Same of `TaskOption.of`. - factory TaskOption.some(R r) => TaskOption(() async => Option.of(r)); - - /// Build a [TaskOption] that returns a [None]. - factory TaskOption.none() => TaskOption(() async => const Option.none()); - - /// Build a [TaskOption] from the result of running `task`. - factory TaskOption.fromTask(Task<R> task) => - TaskOption(() async => Option.of(await task.run())); - - /// If `r` is `null`, then return [None]. - /// Otherwise return `Right(r)`. - factory TaskOption.fromNullable(R? r) => - Option.fromNullable(r).toTaskOption(); - - /// When calling `predicate` with `value` returns `true`, then running [TaskOption] returns `Some(value)`. - /// Otherwise return [None]. - factory TaskOption.fromPredicate(R value, bool Function(R a) predicate) => - TaskOption( - () async => predicate(value) ? Option.of(value) : const Option.none(), - ); - - /// Converts a [Future] that may throw to a [Future] that never throws - /// but returns a [Option] instead. - /// - /// Used to handle asynchronous computations that may throw using [Option]. - factory TaskOption.tryCatch(Future<R> Function() run) => - TaskOption<R>(() async { - try { - return Option.of(await run()); - } catch (_) { - return const Option.none(); - } - }); - - /// {@template fpdart_traverse_list_task_option} - /// Map each element in the list to a [TaskOption] using the function `f`, - /// and collect the result in an `TaskOption<List<B>>`. - /// - /// Each [TaskOption] is executed in parallel. This strategy is faster than - /// sequence, but **the order of the request is not guaranteed**. - /// - /// If you need [TaskOption] to be executed in sequence, use `traverseListWithIndexSeq`. - /// {@endtemplate} - /// - /// Same as `TaskOption.traverseList` but passing `index` in the map function. - static TaskOption<List<B>> traverseListWithIndex<A, B>( - List<A> list, - TaskOption<B> Function(A a, int i) f, - ) => - TaskOption<List<B>>( - () async => Option.sequenceList( - await Task.traverseListWithIndex<A, Option<B>>( - list, - (a, i) => Task(() => f(a, i).run()), - ).run(), - ), - ); - - /// {@macro fpdart_traverse_list_task_option} - /// - /// Same as `TaskOption.traverseListWithIndex` but without `index` in the map function. - static TaskOption<List<B>> traverseList<A, B>( - List<A> list, - TaskOption<B> Function(A a) f, - ) => - traverseListWithIndex<A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_task_option} - /// Convert a `List<TaskOption<A>>` to a single `TaskOption<List<A>>`. - /// - /// Each [TaskOption] will be executed in parallel. - /// - /// If you need [TaskOption] to be executed in sequence, use `sequenceListSeq`. - /// {@endtemplate} - static TaskOption<List<A>> sequenceList<A>( - List<TaskOption<A>> list, - ) => - traverseList(list, identity); - - /// {@template fpdart_traverse_list_seq_task_option} - /// Map each element in the list to a [TaskOption] using the function `f`, - /// and collect the result in an `TaskOption<List<B>>`. - /// - /// Each [TaskOption] is executed in sequence. This strategy **takes more time than - /// parallel**, but it ensures that all the request are executed in order. - /// - /// If you need [TaskOption] to be executed in parallel, use `traverseListWithIndex`. - /// {@endtemplate} - /// - /// Same as `TaskOption.traverseListSeq` but passing `index` in the map function. - static TaskOption<List<B>> traverseListWithIndexSeq<A, B>( - List<A> list, - TaskOption<B> Function(A a, int i) f, - ) => - TaskOption<List<B>>( - () async => Option.sequenceList( - await Task.traverseListWithIndexSeq<A, Option<B>>( - list, - (a, i) => Task(() => f(a, i).run()), - ).run(), - ), - ); - - /// {@macro fpdart_traverse_list_seq_task_option} - /// - /// Same as `TaskOption.traverseListWithIndexSeq` but without `index` in the map function. - static TaskOption<List<B>> traverseListSeq<A, B>( - List<A> list, - TaskOption<B> Function(A a) f, - ) => - traverseListWithIndexSeq<A, B>(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_seq_task_option} - /// Convert a `List<TaskOption<A>>` to a single `TaskOption<List<A>>`. - /// - /// Each [TaskOption] will be executed in sequence. - /// - /// If you need [TaskOption] to be executed in parallel, use `sequenceList`. - /// {@endtemplate} - static TaskOption<List<A>> sequenceListSeq<A>( - List<TaskOption<A>> list, - ) => - traverseListSeq(list, identity); - - /// Build a [TaskOption] from `either` that returns [None] when - /// `either` is [Left], otherwise it returns [Some]. - static TaskOption<R> fromEither<L, R>(Either<L, R> either) => - TaskOption(() async => either.match((_) => const Option.none(), some)); - - /// Converts a [Future] that may throw to a [Future] that never throws - /// but returns a [Option] instead. - /// - /// Used to handle asynchronous computations that may throw using [Option]. - /// - /// It wraps the `TaskOption.tryCatch` factory to make chaining with `flatMap` - /// easier. - static TaskOption<R> Function(A a) tryCatchK<R, A>( - Future<R> Function(A a) run) => - (a) => TaskOption.tryCatch(() => run(a)); -} diff --git a/packages/fpdart/lib/src/typeclass/alt.dart b/packages/fpdart/lib/src/typeclass/alt.dart deleted file mode 100644 index 6227f16..0000000 --- a/packages/fpdart/lib/src/typeclass/alt.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'functor.dart'; -import 'hkt.dart'; - -/// `Alt` type class identifies an associative operation on a type constructor. -/// -/// It provides an `alt` function used to return an alternative value when the -/// current one represents a failure (for example, [None] for [Option]). -mixin Alt<KT, A> on HKT<KT, A>, Functor<KT, A> { - HKT<KT, A> alt(HKT<KT, A> Function() orElse); -} - -mixin Alt2<KT, A, B> on HKT2<KT, A, B>, Functor2<KT, A, B> { - HKT2<KT, A, B> alt(HKT2<KT, A, B> Function() orElse); -} - -mixin Alt3<KT, P1, P2, P3> on HKT3<KT, P1, P2, P3>, Functor3<KT, P1, P2, P3> { - HKT3<KT, P1, P2, P3> alt(HKT3<KT, P1, P2, P3> Function() orElse); -} diff --git a/packages/fpdart/lib/src/typeclass/applicative.dart b/packages/fpdart/lib/src/typeclass/applicative.dart deleted file mode 100644 index dac1793..0000000 --- a/packages/fpdart/lib/src/typeclass/applicative.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'functor.dart'; -import 'hkt.dart'; - -mixin Applicative<G, A> on HKT<G, A>, Functor<G, A> { - HKT<G, B> pure<B>(B b); - HKT<G, B> ap<B>(HKT<G, B Function(A a)> a); - - @override - HKT<G, B> map<B>(B Function(A a) f) => ap(pure(f)); -} - -mixin Applicative2<G, A, B> on HKT2<G, A, B>, Functor2<G, A, B> { - HKT2<G, A, C> pure<C>(C c); - HKT2<G, A, C> ap<C>(HKT2<G, A, C Function(B a)> a); - - @override - HKT2<G, A, C> map<C>(C Function(B a) f) => ap(pure(f)); -} - -mixin Applicative3<G, A, B, C> on HKT3<G, A, B, C>, Functor3<G, A, B, C> { - HKT3<G, A, B, D> pure<D>(D a); - HKT3<G, A, B, D> ap<D>(HKT3<G, A, B, D Function(C c)> a); - - @override - HKT3<G, A, B, D> map<D>(D Function(C c) f) => ap(pure(f)); -} diff --git a/packages/fpdart/lib/src/typeclass/band.dart b/packages/fpdart/lib/src/typeclass/band.dart deleted file mode 100644 index a38f661..0000000 --- a/packages/fpdart/lib/src/typeclass/band.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'semigroup.dart'; - -/// Bands are semigroups whose operation -/// (i.e. `combine`) is also [**idempotent**](https://en.wikipedia.org/wiki/Idempotence) -/// (an operation that can be applied multiple times without changing the result beyond the initial application). -mixin Band<T> on Semigroup<T> { - /// Only apply `combine` operation the first time: - /// - `n == 1`, then return `a` - /// - Otherwise return `combine(a, a)` - @override - T combineN(T a, int n) { - if (n <= 0) { - throw const FormatException( - "Repeated combining for bands must have n > 0"); - } - - return n == 1 ? a : combine(a, a); - } - - /// Create a `Band` instance from the given function. - static Band<A> instance<A>(A Function(A a1, A a2) f) => _Band(f); -} - -class _Band<T> with Semigroup<T>, Band<T> { - final T Function(T x, T y) comb; - - const _Band(this.comb); - - @override - T combine(T x, T y) => comb(x, y); -} diff --git a/packages/fpdart/lib/src/typeclass/bounded_semilattice.dart b/packages/fpdart/lib/src/typeclass/bounded_semilattice.dart deleted file mode 100644 index d2019d5..0000000 --- a/packages/fpdart/lib/src/typeclass/bounded_semilattice.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'band.dart'; -import 'commutative_monoid.dart'; -import 'commutative_semigroup.dart'; -import 'monoid.dart'; -import 'semigroup.dart'; -import 'semilattice.dart'; - -/// A semilattice in which: -/// -/// > For all elements `x` and `y` of `A`, both the -/// > [**least upper bound**](https://en.wikipedia.org/wiki/Infimum_and_supremum) and -/// > [**greatest lower bound**](https://en.wikipedia.org/wiki/Infimum_and_supremum) -/// > of the set `{x, y}` exist. -/// -/// See also [Semilattice] -mixin BoundedSemilattice<T> on CommutativeMonoid<T>, Semilattice<T> { - /// Return a `BoundedSemilattice` that reverses the order. - @override - BoundedSemilattice<T> reverse() => - _BoundedSemilattice(empty, (x, y) => combine(y, x)); - - /// Return `a`, since `combine(a, a) == a` for a semilattice (idempotent). - /// - /// Return `empty` if `n == 0`. - @override - T combineN(T a, int n) { - if (n < 0) { - throw const FormatException( - "Repeated combining for monoids must have n >= 0"); - } else if (n == 0) { - return empty; - } - - return a; - } - - /// Create a `BoundedSemilattice` instance from the given function and empty value. - static BoundedSemilattice<A> instance<A>( - A emptyValue, A Function(A a1, A a2) f) => - _BoundedSemilattice(emptyValue, f); -} - -class _BoundedSemilattice<T> - with - Semigroup<T>, - Monoid<T>, - CommutativeSemigroup<T>, - Band<T>, - Semilattice<T>, - CommutativeMonoid<T>, - BoundedSemilattice<T> { - final T emp; - final T Function(T x, T y) comb; - - const _BoundedSemilattice(this.emp, this.comb); - - @override - T combine(T x, T y) => comb(x, y); - - @override - T get empty => emp; -} diff --git a/packages/fpdart/lib/src/typeclass/commutative_group.dart b/packages/fpdart/lib/src/typeclass/commutative_group.dart deleted file mode 100644 index 237f546..0000000 --- a/packages/fpdart/lib/src/typeclass/commutative_group.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'commutative_monoid.dart'; -import 'commutative_semigroup.dart'; -import 'group.dart'; -import 'monoid.dart'; -import 'semigroup.dart'; - -/// An commutative group (also known as an [**abelian group**](https://en.wikipedia.org/wiki/Abelian_group)) -/// is a group whose combine operation is [**commutative**](https://en.wikipedia.org/wiki/Commutative_property). -/// -/// See also [Group] -mixin CommutativeGroup<T> on Group<T>, CommutativeMonoid<T> { - /// Create a `CommutativeGroup` instance from the given function, empty value, and inverse function. - static CommutativeGroup<A> instance<A>( - A emptyValue, A Function(A a1, A a2) f, A Function(A a) inv) => - _CommutativeGroup(emptyValue, f, inv); -} - -class _CommutativeGroup<T> - with - Semigroup<T>, - CommutativeSemigroup<T>, - Monoid<T>, - CommutativeMonoid<T>, - Group<T>, - CommutativeGroup<T> { - final T Function(T a) inv; - final T emp; - final T Function(T x, T y) comb; - - const _CommutativeGroup(this.emp, this.comb, this.inv); - - @override - T combine(T x, T y) => comb(x, y); - - @override - T get empty => emp; - - @override - T inverse(T a) => inv(a); -} diff --git a/packages/fpdart/lib/src/typeclass/commutative_monoid.dart b/packages/fpdart/lib/src/typeclass/commutative_monoid.dart deleted file mode 100644 index 7982edc..0000000 --- a/packages/fpdart/lib/src/typeclass/commutative_monoid.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'commutative_semigroup.dart'; -import 'monoid.dart'; -import 'semigroup.dart'; - -/// `CommutativeMonoid` represents a commutative monoid. -/// -/// A monoid is [**commutative**](https://en.wikipedia.org/wiki/Commutative_property) -/// if for all `x` and `y`, `combine(x, y) == combine(y, x)`. -mixin CommutativeMonoid<T> on Monoid<T>, CommutativeSemigroup<T> { - /// Return a `CommutativeMonoid` that reverses the order. - @override - CommutativeMonoid<T> reverse() => - _CommutativeMonoid(empty, (x, y) => combine(y, x)); - - /// Create a `CommutativeMonoid` instance from the given function and empty value. - static CommutativeMonoid<A> instance<A>( - A emptyValue, A Function(A a1, A a2) f) => - _CommutativeMonoid(emptyValue, f); -} - -class _CommutativeMonoid<T> - with - Semigroup<T>, - CommutativeSemigroup<T>, - Monoid<T>, - CommutativeMonoid<T> { - final T emp; - final T Function(T x, T y) comb; - - const _CommutativeMonoid(this.emp, this.comb); - - @override - T combine(T x, T y) => comb(x, y); - - @override - T get empty => emp; -} diff --git a/packages/fpdart/lib/src/typeclass/commutative_semigroup.dart b/packages/fpdart/lib/src/typeclass/commutative_semigroup.dart deleted file mode 100644 index 6a24ebd..0000000 --- a/packages/fpdart/lib/src/typeclass/commutative_semigroup.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'semigroup.dart'; - -/// `CommutativeSemigroup` represents a commutative semigroup. -/// -/// A semigroup is [**commutative**](https://en.wikipedia.org/wiki/Commutative_property) -/// if for all `x` and `y`, `combine(x, y) == combine(y, x)`. -mixin CommutativeSemigroup<T> on Semigroup<T> { - /// Create a `CommutativeSemigroup` instance from the given function. - // ignore: library_private_types_in_public_api - static _CommutativeSemigroup<A> instance<A>(A Function(A a1, A a2) f) => - _CommutativeSemigroup(f); -} - -class _CommutativeSemigroup<T> with Semigroup<T>, CommutativeSemigroup<T> { - final T Function(T x, T y) comb; - - const _CommutativeSemigroup(this.comb); - - @override - T combine(T x, T y) => comb(x, y); -} diff --git a/packages/fpdart/lib/src/typeclass/eq.dart b/packages/fpdart/lib/src/typeclass/eq.dart deleted file mode 100644 index 6e82899..0000000 --- a/packages/fpdart/lib/src/typeclass/eq.dart +++ /dev/null @@ -1,131 +0,0 @@ -/// A type class used to determine equality between 2 instances **of the same type `T`**. -/// Any 2 instances `x` and `y` are equal if `eqv(x, y)` is `true`. -/// -/// Moreover, `eqv` should form an [equivalence relation](https://en.wikipedia.org/wiki/Equivalence_relation) -/// (a binary relation that is reflexive, symmetric, and transitive). -abstract class Eq<T> { - const Eq(); - - /// Returns `true` if `x` and `y` are equivalent, `false` otherwise. - bool eqv(T x, T y); - - /// Returns `false` if `x` and `y` are equivalent, `true` otherwise. - bool neqv(T x, T y) => !eqv(x, y); - - /// Return an `Eq` that gives the result of **and** of `eq1` and `eq2`. - Eq<T> and(Eq<T> eq) => _Eq( - (x, y) => eqv(x, y) && eq.eqv(x, y), - ); - - /// Return an `Eq` that gives the result of **or** of this [Eq] and `eq`. - Eq<T> or(Eq<T> eq) => _Eq( - (x, y) => eqv(x, y) || eq.eqv(x, y), - ); - - /// Return an `Eq` that gives the result of **xor** of this [Eq] and `eq`. - Eq<T> xor(Eq<T> eq) => _Eq( - (x, y) { - final eqThis = eqv(x, y); - final eqOther = eq.eqv(x, y); - return eqThis ? !eqOther : eqOther; - }, - ); - - /// Return an [Eq] instance based on a parameter of type `T` extracted from a class `A`. - /// ```dart - /// class Parent { - /// final int value1; - /// final double value2; - /// const Parent(this.value1, this.value2); - /// } - - /// /// Equality for values of type [Parent] based on their `value1` ([int]). - /// final eqParentInt = Eq.eqInt.contramap<Parent>( - /// (p) => p.value1, - /// ); - - /// /// Equality for of type [Parent] based on their `value2` ([double]). - /// final eqParentDouble = Eq.eqDouble.contramap<Parent>( - /// (p) => p.value2, - /// ); - /// ``` - Eq<A> contramap<A>(T Function(A) map) => _Eq<A>( - (a1, a2) => eqv(map(a1), map(a2)), - ); - - /// Convert an implicit `Eq<B>` to an `Eq<A>` using the given function `f`. - /// - /// ```dart - /// /// Convert an `Eq` on `int` to an `Eq` to check `String` length - /// final instance = Eq.instance<int>((a1, a2) => a1 == a2); - /// final by = Eq.by<String, int>((a) => a.length, instance); - /// - /// expect(by.eqv('abc', 'abc'), true); // Same length - /// expect(by.eqv('abc', 'ab'), false); // Different length - /// ``` - static Eq<A> by<A, B>(B Function(A a) f, Eq<B> eq) => - _Eq((x, y) => eq.eqv(f(x), f(y))); - - /// Create an `Eq` instance from an `eqv` implementation. - /// - /// ```dart - /// final instance = Eq.instance<String>((a1, a2) => a1.substring(0, 2) == a2.substring(0, 2)); - /// - /// expect(instance.eqv('abc', 'abc'), true); // Same 2 characters prefix - /// expect(instance.eqv('abc', 'acb'), false); // Different 2 characters prefix - /// ``` - static Eq<A> instance<A>(bool Function(A a1, A a2) f) => _Eq(f); - - /// An `Eq<A>` that delegates to universal equality (`==`). - static Eq<A> fromUniversalEquals<A>() => _Eq((x, y) => x == y); - - /// An `Eq<A>` in which everything is the same - /// (calling `eqv` returns always `true`). - static Eq<A> allEqual<A>() => _Eq((x, y) => true); - - /// Instance of `Eq` for `num` using the standard `==` operator. - static Eq<num> eqNum = _Eq((x, y) => x == y); - - /// Instance of `Eq` for `int` using the standard `==` operator. - static Eq<int> eqInt = _Eq((x, y) => x == y); - - /// Instance of `Eq` for `double` using the standard `==` operator. - static Eq<double> eqDouble = _Eq((x, y) => x == y); - - /// Instance of `Eq` for `String` using the standard `==` operator. - static Eq<String> eqString = _Eq((x, y) => x == y); - - /// Instance of `Eq` for `bool` using the standard `==` operator. - static Eq<bool> eqBool = _Eq((x, y) => x == y); - - /// [Eq] instance to compare [DateTime] years. - static Eq<DateTime> dateEqYear = _Eq<DateTime>( - (a1, a2) => a1.year == a2.year, - ); - - /// [Eq] instance to compare [DateTime] months. - static Eq<DateTime> dateEqMonth = _Eq<DateTime>( - (a1, a2) => a1.month == a2.month, - ); - - /// [Eq] instance to compare [DateTime] days. - static Eq<DateTime> dateEqDay = _Eq<DateTime>( - (a1, a2) => a1.day == a2.day, - ); - - /// [Eq] instance to compare [DateTime] by year, month, and day. - static Eq<DateTime> dateEqYearMonthDay = _Eq<DateTime>( - (a1, a2) => - dateEqYear.eqv(a1, a2) && - dateEqMonth.eqv(a1, a2) && - dateEqDay.eqv(a1, a2), - ); -} - -class _Eq<T> extends Eq<T> { - final bool Function(T x, T y) eq; - const _Eq(this.eq); - - @override - bool eqv(T x, T y) => eq(x, y); -} diff --git a/packages/fpdart/lib/src/typeclass/extend.dart b/packages/fpdart/lib/src/typeclass/extend.dart deleted file mode 100644 index 3824ced..0000000 --- a/packages/fpdart/lib/src/typeclass/extend.dart +++ /dev/null @@ -1,22 +0,0 @@ -import '../function.dart'; -import 'functor.dart'; -import 'hkt.dart'; - -mixin Extend<KT, A> on HKT<KT, A>, Functor<KT, A> { - /// Extend the type by applying function `f` to it. - /// - /// ```dart - /// final option = Some(10); - /// final value = option.extend((t) => t.isSome() ? 'valid' : 'invalid'); // -> Some('valid') - /// ``` - HKT<KT, Z> extend<Z>(Z Function(HKT<KT, A> t) f); - - HKT<KT, HKT<KT, A>> duplicate() => extend(identity); -} - -mixin Extend2<KT, A, B> on HKT2<KT, A, B>, Functor2<KT, A, B> { - /// Extend the type by applying function `f` to it. - HKT2<KT, A, Z> extend<Z>(Z Function(HKT2<KT, A, B> t) f); - - HKT2<KT, A, HKT2<KT, A, B>> duplicate() => extend(identity); -} diff --git a/packages/fpdart/lib/src/typeclass/filterable.dart b/packages/fpdart/lib/src/typeclass/filterable.dart deleted file mode 100644 index e0a4663..0000000 --- a/packages/fpdart/lib/src/typeclass/filterable.dart +++ /dev/null @@ -1,20 +0,0 @@ -import '../either.dart'; -import '../option.dart'; -import '../typedef.dart'; -import 'functor.dart'; -import 'hkt.dart'; - -mixin Filterable<KT, A> on HKT<KT, A>, Functor<KT, A> { - /// Filter a data structure based on a boolean predicate. - HKT<KT, A> filter(bool Function(A a) f); - - /// Map over a data structure and filter based on a [Option] predicate. - HKT<KT, Z> filterMap<Z>(Option<Z> Function(A a) f); - - /// Partition a data structure based on a boolean predicate. - Separated<KT, A, A> partition(bool Function(A a) f) => - (filter((a) => !f(a)), filter(f)); - - /// Partition a data structure based on an [Either] predicate. - Separated<KT, Z, Y> partitionMap<Z, Y>(Either<Z, Y> Function(A a) f); -} diff --git a/packages/fpdart/lib/src/typeclass/foldable.dart b/packages/fpdart/lib/src/typeclass/foldable.dart deleted file mode 100644 index 4fb3615..0000000 --- a/packages/fpdart/lib/src/typeclass/foldable.dart +++ /dev/null @@ -1,76 +0,0 @@ -import '../function.dart'; -import '../typedef.dart'; -import 'hkt.dart'; -import 'monoid.dart'; - -mixin Foldable<G, A> on HKT<G, A> { - B foldRight<B>(B b, B Function(B acc, A a) f); - - B foldLeft<B>(B b, B Function(B acc, A a) f) => - foldMap<Endo<B>>(dualEndoMonoid(), (a) => (B b) => f(b, a))(b); - - /// Fold implemented by mapping `A` values into `B` and then - /// combining them using the given `Monoid<B>` instance. - /// - /// Use `Monoid<B>` to provide the initial value of the fold (`empty`) and - /// the `combine` function to combine the accumulator `B` with the value of - /// type `B` computed using the function `f` from type `A` (`f(a)`). - B foldMap<B>(Monoid<B> monoid, B Function(A a) f) => - foldRight(monoid.empty, (b, a) => monoid.combine(f(a), b)); - - B foldRightWithIndex<B>(B b, B Function(int i, B acc, A a) f) => - foldRight<(B, int)>( - (b, length() - 1), - (t, a) => (f(t.$2, t.$1, a), t.$2 - 1), - ).$1; - - B foldLeftWithIndex<B>(B b, B Function(int i, B acc, A a) f) => - foldLeft<(B, int)>( - (b, 0), - (t, a) => (f(t.$2, t.$1, a), t.$2 + 1), - ).$1; - - int length() => foldLeft(0, (b, _) => b + 1); - - bool any(bool Function(A a) predicate) => foldMap(boolOrMonoid(), predicate); - bool all(bool Function(A a) predicate) => foldMap(boolAndMonoid(), predicate); - - A concatenate(Monoid<A> monoid) => foldMap(monoid, identity); - - HKT<G, A> plus(HKT<G, A> v); - - /// Insert a new element `A` at the beginning. - HKT<G, A> prepend(A t); - - /// Insert a new element `A` at the end. - HKT<G, A> append(A t); -} - -mixin Foldable2<G, A, B> on HKT2<G, A, B> { - C foldRight<C>(C b, C Function(C acc, B b) f); - - C foldLeft<C>(C b, C Function(C acc, B b) f) => - foldMap<Endo<C>>(dualEndoMonoid(), (b) => (C c) => f(c, b))(b); - - C foldMap<C>(Monoid<C> monoid, C Function(B b) f) => - foldRight(monoid.empty, (c, b) => monoid.combine(f(b), c)); - - C foldRightWithIndex<C>(C c, C Function(int i, C acc, B b) f) => - foldRight<(C, int)>( - (c, length() - 1), - (t, b) => (f(t.$2, t.$1, b), t.$2 - 1), - ).$1; - - C foldLeftWithIndex<C>(C c, C Function(int i, C acc, B b) f) => - foldLeft<(C, int)>( - (c, 0), - (t, b) => (f(t.$2, t.$1, b), t.$2 + 1), - ).$1; - - int length() => foldLeft(0, (b, _) => b + 1); - - bool any(bool Function(B a) predicate) => foldMap(boolOrMonoid(), predicate); - bool all(bool Function(B a) predicate) => foldMap(boolAndMonoid(), predicate); - - B concatenate(Monoid<B> monoid) => foldMap(monoid, identity); -} diff --git a/packages/fpdart/lib/src/typeclass/functor.dart b/packages/fpdart/lib/src/typeclass/functor.dart deleted file mode 100644 index 648b556..0000000 --- a/packages/fpdart/lib/src/typeclass/functor.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'hkt.dart'; - -/// `Functor<G, A> extends HKT<G, A>` to express the fact that the classes implementing -/// the [Functor] interface will need to be higher kinded types. -mixin Functor<G, A> on HKT<G, A> { - /// Return type is `HKT<G, B>`, which expresses the fact that what - /// we return is using exactly the same type constructor represented by the brand `G` - HKT<G, B> map<B>(B Function(A a) f); -} - -mixin Functor2<G, A, B> on HKT2<G, A, B> { - HKT2<G, A, C> map<C>(C Function(B b) f); -} - -mixin Functor3<G, A, B, C> on HKT3<G, A, B, C> { - HKT3<G, A, B, D> map<D>(D Function(C c) f); -} diff --git a/packages/fpdart/lib/src/typeclass/group.dart b/packages/fpdart/lib/src/typeclass/group.dart deleted file mode 100644 index fd5cb73..0000000 --- a/packages/fpdart/lib/src/typeclass/group.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'monoid.dart'; -import 'semigroup.dart'; - -/// A group is a monoid where each element has an [**inverse**](https://en.wikipedia.org/wiki/Inverse_element). -mixin Group<T> on Monoid<T> { - /// Find the inverse of `a`. - /// - /// `combine(a, inverse(a))` == `combine(inverse(a), a)` == `empty` - T inverse(T a); - - /// Remove the element `b` from `a`. - /// - /// Equivalent to `combine(a, inverse(b))`. - T remove(T a, T b) => combine(a, inverse(b)); - - /// Return `a` appended to itself `n` times. If `n` is negative, then - /// this returns `inverse(a)` appended to itself `n` times. - @override - T combineN(T a, int n) { - if (n > 0) { - return _repeatedCombineN(a, n); - } else if (n == 0) { - return empty; - } - - return _repeatedCombineN(inverse(a), -n); - } - - /// Return `a` combined with itself more than once. - T _repeatedCombineN(T a, int n) => - n == 1 ? a : _repeatedCombineNLoop(a, a, n - 1); - - T _repeatedCombineNLoop(T acc, T source, int k) => k == 1 - ? combine(acc, source) - : _repeatedCombineNLoop(combine(acc, source), source, k - 1); - - /// Create a `Group` instance from the given function, empty value, and inverse function. - static Group<A> instance<A>( - A emptyValue, A Function(A a1, A a2) f, A Function(A a) inv) => - _Group(emptyValue, f, inv); -} - -class _Group<T> with Semigroup<T>, Monoid<T>, Group<T> { - final T Function(T a) inv; - final T emp; - final T Function(T x, T y) comb; - - const _Group(this.emp, this.comb, this.inv); - - @override - T combine(T x, T y) => comb(x, y); - - @override - T get empty => emp; - - @override - T inverse(T a) => inv(a); -} diff --git a/packages/fpdart/lib/src/typeclass/hash.dart b/packages/fpdart/lib/src/typeclass/hash.dart deleted file mode 100644 index f7136c4..0000000 --- a/packages/fpdart/lib/src/typeclass/hash.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'eq.dart'; - -/// A type class used to represent a hashing scheme for objects of a given type. -/// -/// For any two instances `x` and `y` that are considered equivalent under the -/// equivalence relation defined by this object, `hash(x)` should equal `hash(y)`. -abstract class Hash<T> extends Eq<T> { - const Hash(); - - /// Returns the hash code of the given object under this hashing scheme. - int hash(T x); - - /// Constructs a `Hash` instance by using the universal `hashCode` function and the universal equality relation. - static Hash<A> fromUniversalHashCode<A>() => - _Hash((x, y) => x == y, (x) => x.hashCode); -} - -class _Hash<T> extends Hash<T> { - final bool Function(T x, T y) eq; - final int Function(T x) hs; - - const _Hash(this.eq, this.hs); - - @override - bool eqv(T x, T y) => eq(x, y); - - @override - int hash(T x) => hs(x); -} diff --git a/packages/fpdart/lib/src/typeclass/hkt.dart b/packages/fpdart/lib/src/typeclass/hkt.dart deleted file mode 100644 index 7f70d3b..0000000 --- a/packages/fpdart/lib/src/typeclass/hkt.dart +++ /dev/null @@ -1,20 +0,0 @@ -/// https://marcosh.github.io/post/2020/04/15/higher-kinded-types-php-solution.html -/// https://medium.com/@gcanti/higher-kinded-types-in-flow-275b657992b7 - -/// - [A]: Type parameter -/// - [G]: Type constructor -/// -/// Instead of writing `G<A>`, we will write `HKT<G, A>`. -/// This is useful because in the expression `HKT<G, A>`, both `G` and `A` have kind `*`, -/// so we can deal with them with the type system we currently have at our disposal. -abstract class HKT<G, A> { - const HKT(); -} - -abstract class HKT2<G1, G2, A> { - const HKT2(); -} - -abstract class HKT3<G1, G2, G3, A> { - const HKT3(); -} diff --git a/packages/fpdart/lib/src/typeclass/monad.dart b/packages/fpdart/lib/src/typeclass/monad.dart deleted file mode 100644 index a171bb8..0000000 --- a/packages/fpdart/lib/src/typeclass/monad.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'applicative.dart'; -import 'hkt.dart'; - -mixin Monad<KT, A> on HKT<KT, A>, Applicative<KT, A> { - HKT<KT, B> flatMap<B>(HKT<KT, B> Function(A a) f); - - @override - HKT<KT, B> ap<B>(covariant Monad<KT, B Function(A a)> a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - HKT<KT, D> map2<C, D>(Monad<KT, C> mc, D Function(A a, C c) f) => - flatMap((a) => mc.map((c) => f(a, c))); - - HKT<KT, E> map3<C, D, E>( - Monad<KT, C> mc, Monad<KT, D> md, E Function(A a, C c, D d) f) => - flatMap((a) => mc.flatMap((c) => md.map((d) => f(a, c, d)))); - - HKT<KT, B> andThen<B>(HKT<KT, B> Function() then) => flatMap((_) => then()); - - HKT<KT, A> chainFirst<B>(covariant Monad<KT, B> Function(A a) chain) => - flatMap((a) => chain(a).map((b) => a)); - - HKT<KT, B> call<B>(HKT<KT, B> chain) => flatMap((_) => chain); -} - -mixin Monad2<KT, A, B> on HKT2<KT, A, B>, Applicative2<KT, A, B> { - HKT2<KT, A, C> flatMap<C>(HKT2<KT, A, C> Function(B a) f); - - /// Derive `ap` from `flatMap`. - /// - /// Use `flatMap` to extract the value from `a` and from the current [Monad]. - /// If both these values are present, apply the function from `a` to the value - /// of the current [Monad], using `pure` to return the correct type. - @override - HKT2<KT, A, C> ap<C>(covariant Monad2<KT, A, C Function(B a)> a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - HKT2<KT, A, D> map2<C, D>(Monad2<KT, A, C> m1, D Function(B b, C c) f) => - flatMap((b) => m1.map((c) => f(b, c))); - - HKT2<KT, A, E> map3<C, D, E>(Monad2<KT, A, C> m1, Monad2<KT, A, D> m2, - E Function(B b, C c, D d) f) => - flatMap((b) => m1.flatMap((c) => m2.map((d) => f(b, c, d)))); - - HKT2<KT, A, C> andThen<C>(HKT2<KT, A, C> Function() then) => - flatMap((_) => then()); - - HKT2<KT, A, B> chainFirst<C>( - covariant Monad2<KT, A, C> Function(B b) chain) => - flatMap((b) => chain(b).map((c) => b)); - - HKT2<KT, A, C> call<C>(HKT2<KT, A, C> chain) => flatMap((_) => chain); -} - -mixin Monad3<KT, P1, P2, P3> - on HKT3<KT, P1, P2, P3>, Applicative3<KT, P1, P2, P3> { - HKT3<KT, P1, P2, N1> flatMap<N1>(HKT3<KT, P1, P2, N1> Function(P3) f); - - /// Derive `ap` from `flatMap`. - /// - /// Use `flatMap` to extract the value from `a` and from the current [Monad]. - /// If both these values are present, apply the function from `a` to the value - /// of the current [Monad], using `pure` to return the correct type. - @override - HKT3<KT, P1, P2, N1> ap<N1>( - covariant Monad3<KT, P1, P2, N1 Function(P3)> a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - HKT3<KT, P1, P2, N2> map2<N1, N2>( - Monad3<KT, P1, P2, N1> m1, - N2 Function(P3, N1) f, - ) => - flatMap((b) => m1.map((c) => f(b, c))); - - HKT3<KT, P1, P2, N3> map3<N1, N2, N3>( - Monad3<KT, P1, P2, N1> m1, - Monad3<KT, P1, P2, N2> m2, - N3 Function(P3, N1, N2) f, - ) => - flatMap( - (b) => m1.flatMap((c) => m2.map((d) => f(b, c, d))), - ); - - HKT3<KT, P1, P2, N1> andThen<N1>( - HKT3<KT, P1, P2, N1> Function() then, - ) => - flatMap((_) => then()); - - HKT3<KT, P1, P2, P3> chainFirst<N1>( - covariant Monad3<KT, P1, P2, N1> Function(P3) chain, - ) => - flatMap((b) => chain(b).map((c) => b)); - - HKT3<KT, P1, P2, N1> call<N1>(HKT3<KT, P1, P2, N1> chain) => - flatMap((_) => chain); -} diff --git a/packages/fpdart/lib/src/typeclass/monoid.dart b/packages/fpdart/lib/src/typeclass/monoid.dart deleted file mode 100644 index 19080fe..0000000 --- a/packages/fpdart/lib/src/typeclass/monoid.dart +++ /dev/null @@ -1,81 +0,0 @@ -import '../function.dart'; -import '../typedef.dart'; -import 'eq.dart'; -import 'semigroup.dart'; - -/// A monoid is a semigroup with an identity (`empty`). -/// -/// A monoid is a specialization of a -/// semigroup, so its operation must be **associative**. -/// -/// Additionally, `combine(x, empty) == combine(empty, x) == x`. -/// -/// For example, if we have `Monoid<String>`, -/// with `combine` as string concatenation, then `empty = ""`: -/// -/// ```dart -/// final instance = Monoid.instance<String>('', (a1, a2) => '$a1$a2'); -/// -/// expect(instance.combine('abc', instance.empty), instance.combine(instance.empty, 'abc')); -/// expect(instance.combine('abc', instance.empty), 'abc'); -/// ``` -mixin Monoid<T> on Semigroup<T> { - /// Return the identity element for this monoid. - T get empty; - - /// Tests if `a` is the identity (`empty`). - bool isEmpty(T a, Eq<T> eq) => eq.eqv(a, empty); - - /// Return `a` appended to itself `n` times. - /// - /// Return `empty` if `n == 0`. - @override - T combineN(T a, int n) { - if (n < 0) { - throw const FormatException( - "Repeated combining for monoids must have n >= 0"); - } else if (n == 0) { - return empty; - } - - return _repeatedCombineN(a, n); - } - - /// Return `a` combined with itself more than once. - T _repeatedCombineN(T a, int n) => - n == 1 ? a : _repeatedCombineNLoop(a, a, n - 1); - - T _repeatedCombineNLoop(T acc, T source, int k) => k == 1 - ? combine(acc, source) - : _repeatedCombineNLoop(combine(acc, source), source, k - 1); - - /// Return a `Monoid` that reverses the order. - @override - Monoid<T> reverse() => _Monoid(empty, (x, y) => combine(y, x)); - - /// Create a `Monoid` instance from the given function and empty value. - static Monoid<A> instance<A>(A emptyValue, A Function(A a1, A a2) f) => - _Monoid(emptyValue, f); -} - -class _Monoid<T> with Semigroup<T>, Monoid<T> { - final T emp; - final T Function(T x, T y) comb; - - const _Monoid(this.emp, this.comb); - - @override - T combine(T x, T y) => comb(x, y); - - @override - T get empty => emp; -} - -Monoid<Endo<A>> endoMonoid<A>() => - Monoid.instance<Endo<A>>(identity, (e1, e2) => (A a) => e1(e2(a))); - -Monoid<Endo<A>> dualEndoMonoid<A>() => - Monoid.instance<Endo<A>>(identity, (e1, e2) => (A a) => e2(e1(a))); - -Monoid<bool> boolOrMonoid() => Monoid.instance(false, (a1, a2) => a1 || a2); -Monoid<bool> boolAndMonoid() => Monoid.instance(true, (a1, a2) => a1 && a2); diff --git a/packages/fpdart/lib/src/typeclass/order.dart b/packages/fpdart/lib/src/typeclass/order.dart deleted file mode 100644 index a3f0776..0000000 --- a/packages/fpdart/lib/src/typeclass/order.dart +++ /dev/null @@ -1,151 +0,0 @@ -import 'partial_order.dart'; - -/// The `Order` type class is used to define a [total ordering](https://en.wikipedia.org/wiki/Total_order) -/// on some type `A`. -/// -/// An order is defined by a relation <=, which obeys the following laws: -/// -/// - either `x <= y` or `y <= x` (totality) -/// - if `x <= y` and `y <= x`, then `x == y` (antisymmetry) -/// - if `x <= y` and `y <= z`, then `x <= z` (transitivity) -/// -/// The truth table for compare is defined as follows: -/// -/// | x <= y | x >= y | int | | -/// | :-- | :-- | --: | :-- | -/// | true | true | = 0 | (corresponds to x == y) | -/// | true | false | < 0 | (corresponds to x < y) | -/// | false | true | > 0 | (corresponds to x > y) | -/// -/// **By the totality law, `x <= y` and `y <= x` cannot be both false**. -abstract class Order<T> extends PartialOrder<T> { - const Order(); - - /// Result of comparing `x` with `y`. Returns an Int whose sign is: - /// - negative iff `x < y` - /// - zero iff `x == y` - /// - positive iff `x > y` - int compare(T x, T y); - - @override - double partialCompare(T x, T y) => compare(x, y).toDouble(); - - /// If `x < y`, return `x`, else return `y`. - T min(T x, T y) => lt(x, y) ? x : y; - - /// If `x > y`, return `x`, else return `y`. - T max(T x, T y) => gt(x, y) ? x : y; - - /// Test whether `value` is between `min` and `max` (**inclusive**). - bool between(T min, T max, T value) => gteqv(value, min) && lteqv(value, max); - - /// Clamp `value` between `min` and `max`. - T clamp(T min, T max, T value) => this.max(this.min(value, max), min); - - /// Return an [Order] instance based on a parameter of type `T` extracted from a class `A`. - /// ```dart - /// class Parent { - /// final int value1; - /// final double value2; - /// const Parent(this.value1, this.value2); - /// } - /// - /// /// Order values of type [Parent] based on their `value1` ([int]). - /// final orderParentInt = Order.orderInt.contramap<Parent>( - /// (p) => p.value1, - /// ); - /// - /// /// Order values of type [Parent] based on their `value2` ([double]). - /// final orderParentDouble = Order.orderDouble.contramap<Parent>( - /// (p) => p.value2, - /// ); - /// ``` - @override - Order<A> contramap<A>(T Function(A) map) => Order.from<A>( - (a1, a2) => compare(map(a1), map(a2)), - ); - - /// Return an [Order] reversed. - Order<T> get reverse => _Order((x, y) => compare(y, x)); - - @override - bool eqv(T x, T y) => compare(x, y) == 0; - - @override - bool lteqv(T x, T y) => compare(x, y) <= 0; - - @override - bool lt(T x, T y) => compare(x, y) < 0; - - @override - bool gteqv(T x, T y) => compare(x, y) >= 0; - - @override - bool gt(T x, T y) => compare(x, y) > 0; - - /// Convert an implicit `Order<B>` to an `Order<A>` using the given - /// function `f`. - static Order<A> by<A, B>(B Function(A a) f, Order<B> ord) => - _Order((x, y) => ord.compare(f(x), f(y))); - - /// Returns a new `Order<A>` instance that first compares by the first - /// `Order` instance and uses the second `Order` instance to "break ties". - /// - /// That is, `Order.whenEqual(x, y)` creates an `Order` that first orders by `x` and - /// then (if two elements are equal) falls back to `y` for the comparison. - static Order<A> whenEqual<A>(Order<A> first, Order<A> second) => - _Order((x, y) { - final ord = first.compare(x, y); - return ord == 0 ? second.compare(x, y) : ord; - }); - - /// Define an `Order<A>` using the given function `f`. - static Order<A> from<A>(int Function(A a1, A a2) f) => _Order(f); - - /// Define an `Order<A>` using the given 'less than' function `f`. - static Order<A> fromLessThan<A>(bool Function(A a1, A a2) f) => - _Order((x, y) => f(x, y) - ? -1 - : f(y, x) - ? 1 - : 0); - - /// An `Order` instance that considers all `A` instances to be equal - /// (`compare` always returns `0`). - static Order<A> allEqual<A>() => _Order((x, y) => 0); - - /// Instance of `Order` for `int`. - static Order<int> orderInt = _Order((x, y) => x == y - ? 0 - : x > y - ? 1 - : -1); - - /// Instance of `Order` for `num`. - static Order<num> orderNum = _Order((x, y) => x == y - ? 0 - : x > y - ? 1 - : -1); - - /// Instance of `Order` for `double`. - static Order<double> orderDouble = _Order((x, y) => x == y - ? 0 - : x > y - ? 1 - : -1); - - /// Instance of `Order` for [DateTime]. - static Order<DateTime> orderDate = _Order<DateTime>( - (a1, a2) => a1.compareTo(a2), - ); -} - -class _Order<T> extends Order<T> { - final int Function(T x, T y) comp; - - const _Order(this.comp); - - @override - int compare(T x, T y) => comp(x, y); -} diff --git a/packages/fpdart/lib/src/typeclass/partial_order.dart b/packages/fpdart/lib/src/typeclass/partial_order.dart deleted file mode 100644 index 490afe1..0000000 --- a/packages/fpdart/lib/src/typeclass/partial_order.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'eq.dart'; - -/// The `PartialOrder` type class is used to define a -/// [partial ordering](https://en.wikipedia.org/wiki/Partially_ordered_set) on some type `A`. -/// -/// A partial order is defined by a relation <=, which obeys the following laws: -/// - x <= x (reflexivity) -/// - if x <= y and y <= x, then x == y (anti-symmetry) -/// - if x <= y and y <= z, then x <= z (transitivity) -/// -/// To compute both <= and >= at the same time, we use a `double` number -/// to encode the result of the comparisons x <= y and x >= y. -/// -/// The truth table is defined as follows: -/// -/// | x <= y | x >= y | result | note | -/// | :-- | :-- | --: | :-- | -/// | true |true | 0.0 | (corresponds to x == y) | -/// | false |false | null | (x and y cannot be compared) | -/// | true |false | -1.0 | (corresponds to x < y) | -/// | false |true | 1.0 | (corresponds to x > y) | -/// -/// **Note**: A partial order under which every pair of elements is comparable -/// is called a [total order](https://en.wikipedia.org/wiki/Total_order) ([Order]). -abstract class PartialOrder<T> extends Eq<T> { - const PartialOrder(); - - /// Result of comparing `x` with `y`. - /// - /// Returns `null` if operands are not comparable. - /// If operands are comparable, returns a `double` whose - /// sign is: - /// - negative iff `x < y` - /// - zero iff `x == y` - /// - positive iff `x > y` - double? partialCompare(T x, T y); - - @override - PartialOrder<A> contramap<A>(T Function(A) map) => PartialOrder.from<A>( - (a1, a2) => partialCompare(map(a1), map(a2)), - ); - - /// Returns `true` if `x` == `y`, `false` otherwise. - @override - bool eqv(T x, T y) { - final c = partialCompare(x, y); - return c != null && c == 0; - } - - /// Returns `true` if `x` <= `y`, `false` otherwise. - bool lteqv(T x, T y) { - final c = partialCompare(x, y); - return c != null && c <= 0; - } - - /// Returns `true` if `x` < `y`, `false` otherwise. - bool lt(T x, T y) { - final c = partialCompare(x, y); - return c != null && c < 0; - } - - /// Returns `true` if `x` >= `y`, `false` otherwise. - bool gteqv(T x, T y) { - final c = partialCompare(x, y); - return c != null && c >= 0; - } - - /// Returns `true` if `x` > `y`, `false` otherwise. - bool gt(T x, T y) { - final c = partialCompare(x, y); - return c != null && c > 0; - } - - /// Convert an implicit `PartialOrder[B]` to an `PartialOrder[A]` using the given - /// function `f`. - static PartialOrder<A> by<A, B>(B Function(A a) f, PartialOrder<B> po) => - _PartialOrder<A>((x, y) => po.partialCompare(f(x), f(y))); - - /// Defines a partial order on `A` from `p` where all arrows switch direction. - static PartialOrder<A> reverse<A>(PartialOrder<A> p) => - _PartialOrder<A>((x, y) => p.partialCompare(y, x)); - - /// Define a `PartialOrder[A]` using the given function `f`. - static PartialOrder<A> from<A>(double? Function(A a1, A a2) f) => - _PartialOrder<A>(f); -} - -class _PartialOrder<T> extends PartialOrder<T> { - final double? Function(T x, T y) partialO; - - const _PartialOrder(this.partialO); - - @override - double? partialCompare(T x, T y) => partialO(x, y); -} diff --git a/packages/fpdart/lib/src/typeclass/semigroup.dart b/packages/fpdart/lib/src/typeclass/semigroup.dart deleted file mode 100644 index c8e814a..0000000 --- a/packages/fpdart/lib/src/typeclass/semigroup.dart +++ /dev/null @@ -1,72 +0,0 @@ -/// A semigroup is any set `A` with an [**associative operation**](https://en.wikipedia.org/wiki/Associative_property) (`combine`). -/// -/// `(xy)z = x(yz) = xyz` for all `x`, `y`, `z` in `A` -mixin Semigroup<T> { - /// Associative operation which combines two values. - /// - /// ```dart - /// final instance = Semigroup.instance<String>((a1, a2) => '$a1$a2'); - /// - /// expect(instance.combine('a', 'b'), 'ab'); - /// ``` - T combine(T x, T y); - - /// Return `a` combined with itself `n` times. - T combineN(T a, int n) { - if (n <= 0) { - throw const FormatException( - "Repeated combining for semigroups must have n > 0"); - } - - return _repeatedCombineN(a, n); - } - - /// Return `a` combined with itself more than once. - T _repeatedCombineN(T a, int n) => - n == 1 ? a : _repeatedCombineNLoop(a, a, n - 1); - - T _repeatedCombineNLoop(T acc, T source, int k) => k == 1 - ? combine(acc, source) - : _repeatedCombineNLoop(combine(acc, source), source, k - 1); - - /// Return a `Semigroup` that reverses the order. - /// - /// ```dart - /// final instance = Semigroup.instance<String>((a1, a2) => '$a1$a2'); - /// final reverse = instance.reverse(); - /// - /// expect(reverse.combine('a', 'b'), 'ba'); - /// expect(reverse.combine('a', 'b'), instance.combine('b', 'a')); - /// ``` - Semigroup<T> reverse() => _Semigroup((x, y) => combine(y, x)); - - /// Return a `Semigroup` which inserts `middle` between each pair of elements. - /// - /// ```dart - /// final instance = Semigroup.instance<String>((a1, a2) => '$a1$a2'); - /// final intercalate = instance.intercalate('-'); - /// - /// expect(intercalate.combine('a', 'b'), 'a-b'); - /// expect(intercalate.combineN('a', 3), 'a-a-a'); - /// ``` - Semigroup<T> intercalate(T middle) => - _Semigroup((x, y) => combine(x, combine(middle, y))); - - /// Create a `Semigroup` instance from the given function. - static Semigroup<A> instance<A>(A Function(A a1, A a2) f) => _Semigroup(f); - - /// Create a `Semigroup` instance that always returns the lefthand side. - static Semigroup<A> first<A>() => _Semigroup((x, y) => x); - - /// Create a `Semigroup` instance that always returns the righthand side. - static Semigroup<A> last<A>() => _Semigroup((x, y) => y); -} - -class _Semigroup<T> with Semigroup<T> { - final T Function(T x, T y) comb; - - const _Semigroup(this.comb); - - @override - T combine(T x, T y) => comb(x, y); -} diff --git a/packages/fpdart/lib/src/typeclass/semilattice.dart b/packages/fpdart/lib/src/typeclass/semilattice.dart deleted file mode 100644 index e916876..0000000 --- a/packages/fpdart/lib/src/typeclass/semilattice.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'band.dart'; -import 'commutative_semigroup.dart'; -import 'eq.dart'; -import 'partial_order.dart'; -import 'semigroup.dart'; - -/// [**Semilattices**](https://en.wikipedia.org/wiki/Semilattice) -/// are commutative semigroups whose operation -/// (i.e. `combine`) is also [**idempotent**](https://en.wikipedia.org/wiki/Idempotence). -/// -/// **Meet-semilattice** -/// > For all elements `x` and `y` of `A`, the [**greatest lower bound**](https://en.wikipedia.org/wiki/Infimum_and_supremum) -/// > of the set `{x, y}` exists. -/// -/// **Join-semilattice** -/// > For all elements `x` and `y` of `A`, the [**least upper bound**](https://en.wikipedia.org/wiki/Infimum_and_supremum) -/// > of the set `{x, y}` exists. -/// -/// See also [CommutativeSemigroup], [Bind]. -mixin Semilattice<T> on Band<T>, CommutativeSemigroup<T> { - /// Given `Eq<T>`, return a `PartialOrder<T>` using the `combine` - /// operator of `Semilattice` to determine the partial ordering. This method assumes - /// `combine` functions as `meet` (that is, as a **lower bound**). - /// - /// This method returns: - /// - 0.0 if `x == y` - /// - -1.0 if `x == combine(x, y)` - /// - 1.0 if `y == combine(x, y)` - /// - `null` otherwise - PartialOrder<T> asMeetPartialOrder(Eq<T> eq) => PartialOrder.from( - (x, y) { - if (eq.eqv(x, y)) { - return 0; - } - - final z = combine(x, y); - return eq.eqv(x, z) - ? -1 - : eq.eqv(y, z) - ? 1 - : null; - }, - ); - - /// Given `Eq<T>`, return a `PartialOrder<T>` using the `combine` - /// operator of `Semilattice` to determine the partial ordering. This method assumes - /// `combine` functions as `join` (that is, as an **upper bound**). - /// - /// This method returns: - /// - 0.0 if `x == y` - /// - -1.0 if `y == combine(x, y)` - /// - 1.0 if `x == combine(x, y)` - /// - `null` otherwise - PartialOrder<T> asJoinPartialOrder(Eq<T> eq) => PartialOrder.from( - (x, y) { - if (eq.eqv(x, y)) { - return 0; - } - - final z = combine(x, y); - return eq.eqv(y, z) - ? -1 - : eq.eqv(x, z) - ? 1 - : null; - }, - ); - - /// Create a `Semilattice` instance from the given function. - static Semilattice<A> instance<A>(A Function(A a1, A a2) f) => - _Semilattice(f); -} - -class _Semilattice<T> - with Semigroup<T>, CommutativeSemigroup<T>, Band<T>, Semilattice<T> { - final T Function(T x, T y) comb; - - const _Semilattice(this.comb); - - @override - T combine(T x, T y) => comb(x, y); -} diff --git a/packages/fpdart/lib/src/typeclass/typeclass.export.dart b/packages/fpdart/lib/src/typeclass/typeclass.export.dart deleted file mode 100644 index 77145ab..0000000 --- a/packages/fpdart/lib/src/typeclass/typeclass.export.dart +++ /dev/null @@ -1,21 +0,0 @@ -export 'alt.dart'; -export 'applicative.dart'; -export 'band.dart'; -export 'bounded_semilattice.dart'; -export 'commutative_group.dart'; -export 'commutative_monoid.dart'; -export 'commutative_semigroup.dart'; -export 'eq.dart'; -export 'extend.dart'; -export 'filterable.dart'; -export 'foldable.dart'; -export 'functor.dart'; -export 'group.dart'; -export 'hash.dart'; -export 'hkt.dart'; -export 'monad.dart'; -export 'monoid.dart'; -export 'order.dart'; -export 'partial_order.dart'; -export 'semigroup.dart'; -export 'semilattice.dart'; diff --git a/packages/fpdart/lib/src/typedef.dart b/packages/fpdart/lib/src/typedef.dart deleted file mode 100644 index 0d8225f..0000000 --- a/packages/fpdart/lib/src/typedef.dart +++ /dev/null @@ -1,4 +0,0 @@ -import 'typeclass/hkt.dart'; - -typedef Endo<A> = A Function(A a); -typedef Separated<KT, A, B> = (HKT<KT, A>, HKT<KT, B>); From 30e643fbef28117bec0b61bf14626d6e3635fca4 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 13:00:25 +0900 Subject: [PATCH 18/91] remove all examples outdated for now with new `Effect` --- .../bin/do_constructor_pitfalls.dart | 58 - examples/do_constructor_pitfalls/pubspec.yaml | 18 - .../test/do_constructor_pitfalls_test.dart | 1 - .../hello_world/bin/fpdart_hello_world.dart | 66 - examples/hello_world/bin/hello_world.dart | 7 - examples/hello_world/pubspec.yaml | 19 - .../test/fpdart_hello_world_test.dart | 22 - .../hello_world/test/hello_world_test.dart | 13 - examples/json_serializable/.gitattributes | 1 - examples/json_serializable/.gitignore | 7 - examples/json_serializable/lib/main.dart | 1 - examples/json_serializable/lib/user.dart | 20 - examples/json_serializable/pubspec.yaml | 21 - .../json_serializable/test/user_test.dart | 62 - examples/managing_imports/.gitignore | 7 - examples/managing_imports/README.md | 13 - .../managing_imports/analysis_options.yaml | 8 - .../bin/managing_imports.dart | 17 - examples/managing_imports/lib/functional.dart | 13 - examples/managing_imports/pubspec.yaml | 16 - examples/open_meteo_api/.gitignore | 7 - examples/open_meteo_api/README.md | 26 - examples/open_meteo_api/analysis_options.yaml | 8 - examples/open_meteo_api/build.yaml | 12 - .../open_meteo_api/lib/open_meteo_api.dart | 5 - .../lib/src/fpdart/location_failure.dart | 65 - .../fpdart/open_meteo_api_client_fpdart.dart | 141 -- .../lib/src/fpdart/weather_failure.dart | 54 - .../lib/src/models/location.dart | 21 - .../open_meteo_api/lib/src/models/models.dart | 2 - .../lib/src/models/weather.dart | 15 - .../lib/src/open_meteo_api_client.dart | 84 - examples/open_meteo_api/pubspec.yaml | 21 - .../test/open_meteo_api_client_test.dart | 202 -- .../open_meteo_api_client_test_fpdart.dart | 249 --- examples/pokeapi_functional/.gitattributes | 2 - examples/pokeapi_functional/.gitignore | 48 - examples/pokeapi_functional/README.md | 9 - .../pokeapi_functional/android/.gitignore | 11 - .../android/app/build.gradle | 59 - .../android/app/src/debug/AndroidManifest.xml | 7 - .../android/app/src/main/AndroidManifest.xml | 41 - .../pokeapi_functional/MainActivity.kt | 6 - .../res/drawable-v21/launch_background.xml | 12 - .../main/res/drawable/launch_background.xml | 12 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 0 bytes .../app/src/main/res/values-night/styles.xml | 18 - .../app/src/main/res/values/styles.xml | 18 - .../app/src/profile/AndroidManifest.xml | 7 - .../pokeapi_functional/android/build.gradle | 29 - .../android/gradle.properties | 3 - .../gradle/wrapper/gradle-wrapper.properties | 6 - .../android/settings.gradle | 11 - examples/pokeapi_functional/ios/.gitignore | 33 - .../ios/Flutter/AppFrameworkInfo.plist | 26 - .../ios/Flutter/Debug.xcconfig | 1 - .../ios/Flutter/Release.xcconfig | 1 - .../ios/Runner.xcodeproj/project.pbxproj | 471 ----- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 91 - .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../ios/Runner/AppDelegate.swift | 13 - .../AppIcon.appiconset/Contents.json | 122 -- .../Icon-App-1024x1024@1x.png | Bin 10932 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 564 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 1283 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 1588 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 1025 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 1716 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 1920 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 1283 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 1895 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 2665 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 2665 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 3831 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 1888 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 3294 -> 0 bytes .../Icon-App-83.5x83.5@2x.png | Bin 3612 -> 0 bytes .../LaunchImage.imageset/Contents.json | 23 - .../LaunchImage.imageset/LaunchImage.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/README.md | 5 - .../Runner/Base.lproj/LaunchScreen.storyboard | 37 - .../ios/Runner/Base.lproj/Main.storyboard | 26 - .../pokeapi_functional/ios/Runner/Info.plist | 45 - .../ios/Runner/Runner-Bridging-Header.h | 1 - .../lib/api/fetch_pokemon.dart | 67 - .../lib/constants/constants.dart | 7 - .../lib/controllers/pokemon_provider.dart | 33 - examples/pokeapi_functional/lib/main.dart | 75 - .../lib/models/pokemon.dart | 20 - .../lib/models/pokemon.freezed.dart | 234 --- .../pokeapi_functional/lib/models/sprite.dart | 14 - .../lib/models/sprite.freezed.dart | 151 -- examples/pokeapi_functional/pubspec.yaml | 32 - examples/pokeapi_functional/web/favicon.png | Bin 917 -> 0 bytes .../pokeapi_functional/web/icons/Icon-192.png | Bin 5292 -> 0 bytes .../pokeapi_functional/web/icons/Icon-512.png | Bin 8252 -> 0 bytes examples/pokeapi_functional/web/index.html | 98 - examples/pokeapi_functional/web/manifest.json | 23 - examples/read_write_file/.gitignore | 7 - .../read_write_file/assets/source_eng.txt | 8 - .../read_write_file/assets/source_ita.txt | 8 - examples/read_write_file/lib/file_system.dart | 30 - examples/read_write_file/lib/main.dart | 43 - examples/read_write_file/lib/program.dart | 71 - examples/read_write_file/pubspec.yaml | 18 - examples/read_write_file/test/main_test.dart | 34 - .../api_requests/try_catch_validation.dart | 107 -- packages/fpdart/example/do_notation/main.dart | 83 - packages/fpdart/example/effect/main.dart | 16 +- packages/fpdart/example/logger/logger.dart | 78 - packages/fpdart/example/logger/main.dart | 121 -- packages/fpdart/example/main.dart | 123 -- packages/fpdart/example/src/either/cast.dart | 26 - .../example/src/either/chain_either.dart | 88 - .../fpdart/example/src/either/either1.dart | 36 - .../fpdart/example/src/either/overview.dart | 60 - .../src/either/shopping/functional.dart | 81 - .../fpdart/example/src/function/const_f.dart | 8 - .../fpdart/example/src/function/curry.dart | 34 - .../fpdart/example/src/function/identity.dart | 19 - packages/fpdart/example/src/io/overview.dart | 18 - packages/fpdart/example/src/list/fold.dart | 33 - .../fpdart/example/src/list/overview.dart | 15 - packages/fpdart/example/src/list/zip.dart | 8 - packages/fpdart/example/src/map/overview.dart | 24 - .../fpdart/example/src/option/cheat_sheet.md | 120 -- .../src/option/get-price/functional.dart | 21 - .../src/option/get-price/non_functional.dart | 16 - .../src/option/nullable/option_nullable.dart | 64 - .../example/src/option/nullable/overview.dart | 46 - .../fpdart/example/src/option/option1.dart | 30 - .../fpdart/example/src/option/option2.dart | 18 - .../fpdart/example/src/option/option3.dart | 51 - .../fpdart/example/src/option/overview.dart | 66 - .../src/option/shopping/functional.dart | 83 - .../example/src/predicate/overview.dart | 9 - .../fpdart/example/src/pure_function.dart | 41 - .../fpdart/example/src/reader/reader1.dart | 51 - .../fpdart/example/src/reader/reader2.dart | 49 - .../src/reader_task_either/overview.dart | 23 - packages/fpdart/example/src/state/state1.dart | 57 - .../example/src/state_async/state_async1.dart | 12 - .../fpdart/example/src/task/overview.dart | 29 - .../example/src/task/task_and_future.dart | 92 - .../src/task_either/async_flat_map/data.dart | 9 - .../task_either/async_flat_map/failure.dart | 5 - .../src/task_either/async_flat_map/main.dart | 45 - .../async_flat_map/student_repo.dart | 15 - .../fpdart/example/src/task_either/chain.dart | 24 - .../example/src/task_either/finally.dart | 50 - .../example/src/task_either/overview.dart | 57 - .../src/task_either/sync_to_async.dart | 32 - .../src/task_option/future_task_option.dart | 25 - .../example/src/task_option/overview.dart | 8 - .../fpdart/example/src/traverse/option.dart | 18 - .../src/traverse/sequnce_traverse.dart | 21 - .../fpdart/example/src/typeclass/eq/eq1.dart | 19 - .../example/src/typeclass/order/order1.dart | 34 - .../example/src/typeclass/order/order2.dart | 19 - packages/fpdart/lib/src/effect.dart | 23 +- packages/fpdart/test/src/band_test.dart | 20 - .../test/src/bounded_semilattice_test.dart | 40 - .../test/src/commutative_group_test.dart | 31 - .../test/src/commutative_monoid_test.dart | 22 - .../test/src/commutative_semigroup_test.dart | 14 - packages/fpdart/test/src/date_test.dart | 96 - packages/fpdart/test/src/either_test.dart | 1221 ------------ packages/fpdart/test/src/eq_test.dart | 188 -- .../src/extension/curry_extension_test.dart | 110 -- .../extension/date_time_extension_test.dart | 66 - .../extension/iterable_extension_test.dart | 1674 ----------------- .../src/extension/list_extension_test.dart | 35 - .../src/extension/map_extension_test.dart | 627 ------ .../src/extension/option_extension_test.dart | 44 - .../extension/predicate_extension_test.dart | 90 - .../src/extension/string_extension_test.dart | 858 --------- packages/fpdart/test/src/function_test.dart | 66 - packages/fpdart/test/src/group_test.dart | 45 - packages/fpdart/test/src/hash_test.dart | 23 - packages/fpdart/test/src/io_either_test.dart | 774 -------- packages/fpdart/test/src/io_option_test.dart | 579 ------ packages/fpdart/test/src/io_ref_test.dart | 99 - packages/fpdart/test/src/io_test.dart | 222 --- packages/fpdart/test/src/monoid_test.dart | 49 - packages/fpdart/test/src/option_test.dart | 811 -------- packages/fpdart/test/src/order_test.dart | 240 --- .../fpdart/test/src/partial_order_test.dart | 14 - .../test/src/reader_task_either_test.dart | 943 ---------- .../fpdart/test/src/reader_task_test.dart | 228 --- packages/fpdart/test/src/reader_test.dart | 134 -- packages/fpdart/test/src/semigroup_test.dart | 49 - .../fpdart/test/src/semilattice_test.dart | 48 - .../fpdart/test/src/state_async_test.dart | 214 --- packages/fpdart/test/src/state_test.dart | 266 --- .../fpdart/test/src/task_either_test.dart | 1027 ---------- .../fpdart/test/src/task_option_test.dart | 748 -------- packages/fpdart/test/src/task_test.dart | 318 ---- packages/fpdart/test/src/unit_test.dart | 17 - .../fpdart/test/src/utils/async_utils.dart | 15 - .../test/src/utils/collection_utils.dart | 18 - .../fpdart/test/src/utils/glados_utils.dart | 45 - .../fpdart/test/src/utils/match_utils.dart | 21 - packages/fpdart/test/src/utils/utils.dart | 6 - 214 files changed, 26 insertions(+), 17904 deletions(-) delete mode 100644 examples/do_constructor_pitfalls/bin/do_constructor_pitfalls.dart delete mode 100644 examples/do_constructor_pitfalls/pubspec.yaml delete mode 100644 examples/do_constructor_pitfalls/test/do_constructor_pitfalls_test.dart delete mode 100644 examples/hello_world/bin/fpdart_hello_world.dart delete mode 100644 examples/hello_world/bin/hello_world.dart delete mode 100644 examples/hello_world/pubspec.yaml delete mode 100644 examples/hello_world/test/fpdart_hello_world_test.dart delete mode 100644 examples/hello_world/test/hello_world_test.dart delete mode 100644 examples/json_serializable/.gitattributes delete mode 100644 examples/json_serializable/.gitignore delete mode 100644 examples/json_serializable/lib/main.dart delete mode 100644 examples/json_serializable/lib/user.dart delete mode 100644 examples/json_serializable/pubspec.yaml delete mode 100644 examples/json_serializable/test/user_test.dart delete mode 100644 examples/managing_imports/.gitignore delete mode 100644 examples/managing_imports/README.md delete mode 100644 examples/managing_imports/analysis_options.yaml delete mode 100644 examples/managing_imports/bin/managing_imports.dart delete mode 100644 examples/managing_imports/lib/functional.dart delete mode 100644 examples/managing_imports/pubspec.yaml delete mode 100644 examples/open_meteo_api/.gitignore delete mode 100644 examples/open_meteo_api/README.md delete mode 100644 examples/open_meteo_api/analysis_options.yaml delete mode 100644 examples/open_meteo_api/build.yaml delete mode 100644 examples/open_meteo_api/lib/open_meteo_api.dart delete mode 100644 examples/open_meteo_api/lib/src/fpdart/location_failure.dart delete mode 100644 examples/open_meteo_api/lib/src/fpdart/open_meteo_api_client_fpdart.dart delete mode 100644 examples/open_meteo_api/lib/src/fpdart/weather_failure.dart delete mode 100644 examples/open_meteo_api/lib/src/models/location.dart delete mode 100644 examples/open_meteo_api/lib/src/models/models.dart delete mode 100644 examples/open_meteo_api/lib/src/models/weather.dart delete mode 100644 examples/open_meteo_api/lib/src/open_meteo_api_client.dart delete mode 100644 examples/open_meteo_api/pubspec.yaml delete mode 100644 examples/open_meteo_api/test/open_meteo_api_client_test.dart delete mode 100644 examples/open_meteo_api/test/open_meteo_api_client_test_fpdart.dart delete mode 100644 examples/pokeapi_functional/.gitattributes delete mode 100644 examples/pokeapi_functional/.gitignore delete mode 100644 examples/pokeapi_functional/README.md delete mode 100644 examples/pokeapi_functional/android/.gitignore delete mode 100644 examples/pokeapi_functional/android/app/build.gradle delete mode 100644 examples/pokeapi_functional/android/app/src/debug/AndroidManifest.xml delete mode 100644 examples/pokeapi_functional/android/app/src/main/AndroidManifest.xml delete mode 100644 examples/pokeapi_functional/android/app/src/main/kotlin/com/sandromaglione/fpdart/pokeapi/pokeapi_functional/MainActivity.kt delete mode 100644 examples/pokeapi_functional/android/app/src/main/res/drawable-v21/launch_background.xml delete mode 100644 examples/pokeapi_functional/android/app/src/main/res/drawable/launch_background.xml delete mode 100644 examples/pokeapi_functional/android/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 examples/pokeapi_functional/android/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 examples/pokeapi_functional/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 examples/pokeapi_functional/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 examples/pokeapi_functional/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 examples/pokeapi_functional/android/app/src/main/res/values-night/styles.xml delete mode 100644 examples/pokeapi_functional/android/app/src/main/res/values/styles.xml delete mode 100644 examples/pokeapi_functional/android/app/src/profile/AndroidManifest.xml delete mode 100644 examples/pokeapi_functional/android/build.gradle delete mode 100644 examples/pokeapi_functional/android/gradle.properties delete mode 100644 examples/pokeapi_functional/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 examples/pokeapi_functional/android/settings.gradle delete mode 100644 examples/pokeapi_functional/ios/.gitignore delete mode 100644 examples/pokeapi_functional/ios/Flutter/AppFrameworkInfo.plist delete mode 100644 examples/pokeapi_functional/ios/Flutter/Debug.xcconfig delete mode 100644 examples/pokeapi_functional/ios/Flutter/Release.xcconfig delete mode 100644 examples/pokeapi_functional/ios/Runner.xcodeproj/project.pbxproj delete mode 100644 examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 examples/pokeapi_functional/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 examples/pokeapi_functional/ios/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 examples/pokeapi_functional/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 examples/pokeapi_functional/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 examples/pokeapi_functional/ios/Runner/AppDelegate.swift delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png delete mode 100644 examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md delete mode 100644 examples/pokeapi_functional/ios/Runner/Base.lproj/LaunchScreen.storyboard delete mode 100644 examples/pokeapi_functional/ios/Runner/Base.lproj/Main.storyboard delete mode 100644 examples/pokeapi_functional/ios/Runner/Info.plist delete mode 100644 examples/pokeapi_functional/ios/Runner/Runner-Bridging-Header.h delete mode 100644 examples/pokeapi_functional/lib/api/fetch_pokemon.dart delete mode 100644 examples/pokeapi_functional/lib/constants/constants.dart delete mode 100644 examples/pokeapi_functional/lib/controllers/pokemon_provider.dart delete mode 100644 examples/pokeapi_functional/lib/main.dart delete mode 100644 examples/pokeapi_functional/lib/models/pokemon.dart delete mode 100644 examples/pokeapi_functional/lib/models/pokemon.freezed.dart delete mode 100644 examples/pokeapi_functional/lib/models/sprite.dart delete mode 100644 examples/pokeapi_functional/lib/models/sprite.freezed.dart delete mode 100644 examples/pokeapi_functional/pubspec.yaml delete mode 100644 examples/pokeapi_functional/web/favicon.png delete mode 100644 examples/pokeapi_functional/web/icons/Icon-192.png delete mode 100644 examples/pokeapi_functional/web/icons/Icon-512.png delete mode 100644 examples/pokeapi_functional/web/index.html delete mode 100644 examples/pokeapi_functional/web/manifest.json delete mode 100644 examples/read_write_file/.gitignore delete mode 100644 examples/read_write_file/assets/source_eng.txt delete mode 100644 examples/read_write_file/assets/source_ita.txt delete mode 100644 examples/read_write_file/lib/file_system.dart delete mode 100644 examples/read_write_file/lib/main.dart delete mode 100644 examples/read_write_file/lib/program.dart delete mode 100644 examples/read_write_file/pubspec.yaml delete mode 100644 examples/read_write_file/test/main_test.dart delete mode 100644 packages/fpdart/example/api_requests/try_catch_validation.dart delete mode 100644 packages/fpdart/example/do_notation/main.dart delete mode 100644 packages/fpdart/example/logger/logger.dart delete mode 100644 packages/fpdart/example/logger/main.dart delete mode 100644 packages/fpdart/example/src/either/cast.dart delete mode 100644 packages/fpdart/example/src/either/chain_either.dart delete mode 100644 packages/fpdart/example/src/either/either1.dart delete mode 100644 packages/fpdart/example/src/either/overview.dart delete mode 100644 packages/fpdart/example/src/either/shopping/functional.dart delete mode 100644 packages/fpdart/example/src/function/const_f.dart delete mode 100644 packages/fpdart/example/src/function/curry.dart delete mode 100644 packages/fpdart/example/src/function/identity.dart delete mode 100644 packages/fpdart/example/src/io/overview.dart delete mode 100644 packages/fpdart/example/src/list/fold.dart delete mode 100644 packages/fpdart/example/src/list/overview.dart delete mode 100644 packages/fpdart/example/src/list/zip.dart delete mode 100644 packages/fpdart/example/src/map/overview.dart delete mode 100644 packages/fpdart/example/src/option/cheat_sheet.md delete mode 100644 packages/fpdart/example/src/option/get-price/functional.dart delete mode 100644 packages/fpdart/example/src/option/get-price/non_functional.dart delete mode 100644 packages/fpdart/example/src/option/nullable/option_nullable.dart delete mode 100644 packages/fpdart/example/src/option/nullable/overview.dart delete mode 100644 packages/fpdart/example/src/option/option1.dart delete mode 100644 packages/fpdart/example/src/option/option2.dart delete mode 100644 packages/fpdart/example/src/option/option3.dart delete mode 100644 packages/fpdart/example/src/option/overview.dart delete mode 100644 packages/fpdart/example/src/option/shopping/functional.dart delete mode 100644 packages/fpdart/example/src/predicate/overview.dart delete mode 100644 packages/fpdart/example/src/pure_function.dart delete mode 100644 packages/fpdart/example/src/reader/reader1.dart delete mode 100644 packages/fpdart/example/src/reader/reader2.dart delete mode 100644 packages/fpdart/example/src/reader_task_either/overview.dart delete mode 100644 packages/fpdart/example/src/state/state1.dart delete mode 100644 packages/fpdart/example/src/state_async/state_async1.dart delete mode 100644 packages/fpdart/example/src/task/overview.dart delete mode 100644 packages/fpdart/example/src/task/task_and_future.dart delete mode 100644 packages/fpdart/example/src/task_either/async_flat_map/data.dart delete mode 100644 packages/fpdart/example/src/task_either/async_flat_map/failure.dart delete mode 100644 packages/fpdart/example/src/task_either/async_flat_map/main.dart delete mode 100644 packages/fpdart/example/src/task_either/async_flat_map/student_repo.dart delete mode 100644 packages/fpdart/example/src/task_either/chain.dart delete mode 100644 packages/fpdart/example/src/task_either/finally.dart delete mode 100644 packages/fpdart/example/src/task_either/overview.dart delete mode 100644 packages/fpdart/example/src/task_either/sync_to_async.dart delete mode 100644 packages/fpdart/example/src/task_option/future_task_option.dart delete mode 100644 packages/fpdart/example/src/task_option/overview.dart delete mode 100644 packages/fpdart/example/src/traverse/option.dart delete mode 100644 packages/fpdart/example/src/traverse/sequnce_traverse.dart delete mode 100644 packages/fpdart/example/src/typeclass/eq/eq1.dart delete mode 100644 packages/fpdart/example/src/typeclass/order/order1.dart delete mode 100644 packages/fpdart/example/src/typeclass/order/order2.dart delete mode 100644 packages/fpdart/test/src/band_test.dart delete mode 100644 packages/fpdart/test/src/bounded_semilattice_test.dart delete mode 100644 packages/fpdart/test/src/commutative_group_test.dart delete mode 100644 packages/fpdart/test/src/commutative_monoid_test.dart delete mode 100644 packages/fpdart/test/src/commutative_semigroup_test.dart delete mode 100644 packages/fpdart/test/src/date_test.dart delete mode 100644 packages/fpdart/test/src/either_test.dart delete mode 100644 packages/fpdart/test/src/eq_test.dart delete mode 100644 packages/fpdart/test/src/extension/curry_extension_test.dart delete mode 100644 packages/fpdart/test/src/extension/date_time_extension_test.dart delete mode 100644 packages/fpdart/test/src/extension/iterable_extension_test.dart delete mode 100644 packages/fpdart/test/src/extension/list_extension_test.dart delete mode 100644 packages/fpdart/test/src/extension/map_extension_test.dart delete mode 100644 packages/fpdart/test/src/extension/option_extension_test.dart delete mode 100644 packages/fpdart/test/src/extension/predicate_extension_test.dart delete mode 100644 packages/fpdart/test/src/extension/string_extension_test.dart delete mode 100644 packages/fpdart/test/src/function_test.dart delete mode 100644 packages/fpdart/test/src/group_test.dart delete mode 100644 packages/fpdart/test/src/hash_test.dart delete mode 100644 packages/fpdart/test/src/io_either_test.dart delete mode 100644 packages/fpdart/test/src/io_option_test.dart delete mode 100644 packages/fpdart/test/src/io_ref_test.dart delete mode 100644 packages/fpdart/test/src/io_test.dart delete mode 100644 packages/fpdart/test/src/monoid_test.dart delete mode 100644 packages/fpdart/test/src/option_test.dart delete mode 100644 packages/fpdart/test/src/order_test.dart delete mode 100644 packages/fpdart/test/src/partial_order_test.dart delete mode 100644 packages/fpdart/test/src/reader_task_either_test.dart delete mode 100644 packages/fpdart/test/src/reader_task_test.dart delete mode 100644 packages/fpdart/test/src/reader_test.dart delete mode 100644 packages/fpdart/test/src/semigroup_test.dart delete mode 100644 packages/fpdart/test/src/semilattice_test.dart delete mode 100644 packages/fpdart/test/src/state_async_test.dart delete mode 100644 packages/fpdart/test/src/state_test.dart delete mode 100644 packages/fpdart/test/src/task_either_test.dart delete mode 100644 packages/fpdart/test/src/task_option_test.dart delete mode 100644 packages/fpdart/test/src/task_test.dart delete mode 100644 packages/fpdart/test/src/unit_test.dart delete mode 100644 packages/fpdart/test/src/utils/async_utils.dart delete mode 100644 packages/fpdart/test/src/utils/collection_utils.dart delete mode 100644 packages/fpdart/test/src/utils/glados_utils.dart delete mode 100644 packages/fpdart/test/src/utils/match_utils.dart delete mode 100644 packages/fpdart/test/src/utils/utils.dart diff --git a/examples/do_constructor_pitfalls/bin/do_constructor_pitfalls.dart b/examples/do_constructor_pitfalls/bin/do_constructor_pitfalls.dart deleted file mode 100644 index f130b59..0000000 --- a/examples/do_constructor_pitfalls/bin/do_constructor_pitfalls.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -/// This file demonstrates some common pitfalls when using the `Do` constructor in the `fpdart` package. -/// These are practices that should be avoided when using it. - -void main(List<String> args) {} - -/// This function demonstrates that the `Do` constructor should not contain a `throw` statement. -/// Throwing an exception inside a `Do` constructor can lead to unexpected behavior and should be avoided. -/// Instead, use the `Option` type to handle errors. -void doConstructorShouldNotContainThrow() { - const testOption = const Option<String>.of('test'); - Option.Do( - ($) { - if ($(testOption) == 'test') { - // Do not throw inside a Do constructor - throw Exception('Error'); - } - return 'success'; - }, - ); -} - -/// This function demonstrates that the `Do` constructor should not contain an `await` statement without executing the `$` function. -void doConstructorShouldNotAwaitWithoutExecutingDollarFunction() { - Future<String> future = Future.value('test'); - const testOption = const Option<String>.of('test'); - Option.Do( - ($) async { - // Do not use `await` without executing the `$` function - await future; - return $(testOption); - }, - ); -} - -// This function demonstrates that the `Do` constructor should not be nested. -/// Nesting `Do` constructors can lead to unexpected behavior and should be avoided. -void doConstructorShouldNotBeNested() { - const testOption = const Option<String>.of('test'); - // Do not nest Do constructors - Option.Do( - ($) => Option.Do( - ($) => $(testOption), - ), - ); -} - -/// This function demonstrates that the `Do` constructor should not call the `$` function inside a callback. -void doConstructorShouldNotCallDollarFunctionInCallback() { - const testOption = const Option<List<String>>.of(['test']); - Option.Do( - ($) => $(testOption).map( - // Do not call the `$` function inside a callback - (stringValue) => $(optionOf(stringValue)), - ), - ); -} diff --git a/examples/do_constructor_pitfalls/pubspec.yaml b/examples/do_constructor_pitfalls/pubspec.yaml deleted file mode 100644 index bfc18f8..0000000 --- a/examples/do_constructor_pitfalls/pubspec.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: fpdart_do_constructor_pitfalls -publish_to: none -version: 0.1.0 -homepage: https://www.sandromaglione.com/ -repository: https://github.com/SandroMaglione/fpdart -description: Example of Functional programming in Dart and Flutter using fpdart. Pitfalls to avoid when using the do constructor. - -environment: - sdk: ">=3.0.0 <4.0.0" - -dependencies: - fpdart: - path: ../../packages/fpdart - -dev_dependencies: - lint: ^2.1.2 - test: ^1.24.3 - mocktail: ^0.3.0 diff --git a/examples/do_constructor_pitfalls/test/do_constructor_pitfalls_test.dart b/examples/do_constructor_pitfalls/test/do_constructor_pitfalls_test.dart deleted file mode 100644 index ab73b3a..0000000 --- a/examples/do_constructor_pitfalls/test/do_constructor_pitfalls_test.dart +++ /dev/null @@ -1 +0,0 @@ -void main() {} diff --git a/examples/hello_world/bin/fpdart_hello_world.dart b/examples/hello_world/bin/fpdart_hello_world.dart deleted file mode 100644 index c4ec52d..0000000 --- a/examples/hello_world/bin/fpdart_hello_world.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -void helloWorld(String message) { - print("Hello World: $message"); -} - -/// 1️⃣ Pure function (Thunk) -void Function() helloWorld1(String message) => () { - print("Hello World: $message"); - }; - -/// A thunk with no error is called [IO] in `fpdart` -IO<void> helloWorld1Fpdart(String message) => IO(() { - print("Hello World: $message"); - }); - -/// 2️⃣ Explicit error -/// Understand from the return type if and how the function may fail -Either<Never, void> Function() helloWorld2(String message) => () { - print("Hello World: $message"); - return Either.of(null); - }; - -/// A thunk with explicit error [Either] is called [IOEither] in `fpdart` -IOEither<Never, void> helloWorld2Fpdart1(String message) => IOEither(() { - print("Hello World: $message"); - return Either.of(null); - }); - -/// ...or using the `right` constructor -IOEither<Never, void> helloWorld2Fpdart2(String message) => IOEither.right(() { - print("Hello World: $message"); - }); - -/// 3️⃣ Explicit dependency -/// Provide the `print` method as a dependency instead of implicit global function - -abstract class Console { - void log(Object? object); -} - -class ConsoleImpl implements Console { - @override - void log(Object? object) { - print(object); - } -} - -Either<Never, void> Function() Function(Console) helloWorld3(String message) => - (console) => () { - console.log("Hello World: $message"); - return Either.of(null); - }; - -/// Thunk (async) + error + dependency is called [ReaderTaskEither] in `fpdart` -ReaderTaskEither<Console, Never, void> helloWorld3Fpdart(String message) => - ReaderTaskEither((console) async { - console.log("Hello World: $message"); - return Either.of(null); - }); - -void main(List<String> args) { - final definition = helloWorld3("Sandro"); - final thunk = definition(ConsoleImpl()); - thunk(); -} diff --git a/examples/hello_world/bin/hello_world.dart b/examples/hello_world/bin/hello_world.dart deleted file mode 100644 index 4dc74ce..0000000 --- a/examples/hello_world/bin/hello_world.dart +++ /dev/null @@ -1,7 +0,0 @@ -void helloWorld(String message) { - print("Hello World: $message"); -} - -void main(List<String> args) { - helloWorld("Sandro"); -} diff --git a/examples/hello_world/pubspec.yaml b/examples/hello_world/pubspec.yaml deleted file mode 100644 index 7a3a050..0000000 --- a/examples/hello_world/pubspec.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: fpdart_hello_world -publish_to: none -version: 0.1.0 -homepage: https://www.sandromaglione.com/ -repository: https://github.com/SandroMaglione/fpdart -description: Example of Functional programming in Dart and Flutter using fpdart. Write a simple "Hello World" using fpdart. -author: Maglione Sandro <lass.maglio@gmail.com> - -environment: - sdk: ">=3.0.0 <4.0.0" - -dependencies: - fpdart: - path: ../../packages/fpdart - -dev_dependencies: - lint: ^2.1.2 - test: ^1.24.3 - mocktail: ^0.3.0 diff --git a/examples/hello_world/test/fpdart_hello_world_test.dart b/examples/hello_world/test/fpdart_hello_world_test.dart deleted file mode 100644 index 686c853..0000000 --- a/examples/hello_world/test/fpdart_hello_world_test.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:mocktail/mocktail.dart'; -import 'package:test/test.dart'; - -import '../bin/fpdart_hello_world.dart'; - -class ConsoleTest extends Mock implements Console {} - -void main() { - group('helloWorld3Fpdart', () { - test( - 'should call "log" from the "Console" dependency with the correct input', - () async { - final console = ConsoleTest(); - const input = "test"; - - when(() => console.log(any)).thenReturn(null); - await helloWorld3Fpdart(input).run(console); - verify(() => console.log("Hello World: $input")).called(1); - }, - ); - }); -} diff --git a/examples/hello_world/test/hello_world_test.dart b/examples/hello_world/test/hello_world_test.dart deleted file mode 100644 index e0515e3..0000000 --- a/examples/hello_world/test/hello_world_test.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:test/test.dart'; - -void main() { - group('helloWorld', () { - /// `'should call "print" with the correct input'` - /// - /// This is difficult to test, since `print` is an implicit dependency 🙌 - /// - /// Furthermore, `print` will be executed at every test. Imagine having a - /// request to update a production database instead of `print` (both are side-effects), - /// you do not want to interact with a real database with tests ⚠️ - }); -} diff --git a/examples/json_serializable/.gitattributes b/examples/json_serializable/.gitattributes deleted file mode 100644 index 8869490..0000000 --- a/examples/json_serializable/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.g.dart linguist-generated=true diff --git a/examples/json_serializable/.gitignore b/examples/json_serializable/.gitignore deleted file mode 100644 index 570e1df..0000000 --- a/examples/json_serializable/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.dart_tool/ -.packages -pubspec.lock - -# Generated files -**.g.dart -.idea/ \ No newline at end of file diff --git a/examples/json_serializable/lib/main.dart b/examples/json_serializable/lib/main.dart deleted file mode 100644 index ab73b3a..0000000 --- a/examples/json_serializable/lib/main.dart +++ /dev/null @@ -1 +0,0 @@ -void main() {} diff --git a/examples/json_serializable/lib/user.dart b/examples/json_serializable/lib/user.dart deleted file mode 100644 index 0058064..0000000 --- a/examples/json_serializable/lib/user.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:json_annotation/json_annotation.dart'; - -part 'user.g.dart'; - -@JsonSerializable() -class User { - final Option<int> id; - final Option<DateTime> birthDate; - final Option<String> phone; - - const User({ - required this.id, - required this.birthDate, - required this.phone, - }); - - factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); - Map<String, dynamic> toJson() => _$UserToJson(this); -} diff --git a/examples/json_serializable/pubspec.yaml b/examples/json_serializable/pubspec.yaml deleted file mode 100644 index 2a1bfdb..0000000 --- a/examples/json_serializable/pubspec.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: fpdart_json_serializable -publish_to: none -version: 0.1.0 -homepage: https://www.sandromaglione.com/ -repository: https://github.com/SandroMaglione/fpdart -description: Example of using json_serializable with fpdart types. -author: Maglione Sandro <lass.maglio@gmail.com> - -environment: - sdk: ">=2.13.0 <3.0.0" - -dependencies: - json_serializable: ^6.6.1 - fpdart: - path: ../../packages/fpdart - -dev_dependencies: - test: ^1.17.5 - lint: ^2.0.1 - json_annotation: ^4.1.0 - build_runner: ^2.0.6 diff --git a/examples/json_serializable/test/user_test.dart b/examples/json_serializable/test/user_test.dart deleted file mode 100644 index aba2861..0000000 --- a/examples/json_serializable/test/user_test.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:fpdart_json_serializable/user.dart'; -import 'package:test/test.dart'; - -void main() { - group('Option', () { - test('toJson (None)', () { - expect( - User( - id: none(), - birthDate: none(), - phone: none(), - ).toJson(), - { - "id": null, - "birthDate": null, - "phone": null, - }, - ); - }); - - test('toJson (Some)', () { - expect( - User( - id: some(1), - birthDate: some(DateTime(2020)), - phone: some('phone'), - ).toJson(), - { - "id": 1, - "birthDate": "2020-01-01T00:00:00.000", - "phone": "phone", - }, - ); - }); - - test('fromJson (None)', () { - final user = User.fromJson(<String, dynamic>{ - "id": null, - "birthDate": null, - "phone": null, - }); - - expect(user.id, isA<None>()); - expect(user.birthDate, isA<None>()); - expect(user.phone, isA<None>()); - }); - - test('fromJson (Some)', () { - final user = User.fromJson(<String, dynamic>{ - "id": 1, - "birthDate": DateTime(2020), - "phone": "phone", - }); - - expect(user.id, isA<Some<int>>()); - expect(user.id.getOrElse(() => -1), 1); - expect(user.birthDate.getOrElse(() => DateTime(1990)), DateTime(2020)); - expect(user.phone.getOrElse(() => ''), 'phone'); - }); - }); -} diff --git a/examples/managing_imports/.gitignore b/examples/managing_imports/.gitignore deleted file mode 100644 index a2c86b7..0000000 --- a/examples/managing_imports/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Files and directories created by pub. -.dart_tool/ -.packages -pubspec.lock - -# Conventional directory for build output. -build/ diff --git a/examples/managing_imports/README.md b/examples/managing_imports/README.md deleted file mode 100644 index 10b32c0..0000000 --- a/examples/managing_imports/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Managing Imports - -Naming things is hard. Sometimes, the same name gets used for different things. In Dart, naming conflicts can be mitigated through the use of [import prefixes](https://dart.dev/language/libraries#specifying-a-library-prefix), as well as [show and hide operations](https://dart.dev/language/libraries#importing-only-part-of-a-library). - -This is particularly important when using a package like `fpdart` that provides a lot of classes with common names. - -As an example, suppose you decide to use `fpdart` with your Flutter program. You'll quickly discover that `fpdart` uses `State` as a class name, which conflicts with the `State` class in Flutter. - -The solution is to create an import shim that solves both of these problems. We'll call it `functional.dart`. This shim will import `fpdart`, and re-export the classes we want to use. We can rename `fpdart`'s `State` to `FpState` to avoid the conflict. We can then import `functional.dart` instead of `fpdart`. - -`functional.dart` can also hold any other functional programming utilities we want to use. It can also be used to provide or import class extensions and mapping functions between our types and the functional types. - -A one-stop shop for functional programming in Dart! diff --git a/examples/managing_imports/analysis_options.yaml b/examples/managing_imports/analysis_options.yaml deleted file mode 100644 index e5e23e9..0000000 --- a/examples/managing_imports/analysis_options.yaml +++ /dev/null @@ -1,8 +0,0 @@ -include: package:very_good_analysis/analysis_options.yaml -analyzer: - exclude: - - lib/**/*.g.dart - -linter: - rules: - public_member_api_docs: false diff --git a/examples/managing_imports/bin/managing_imports.dart b/examples/managing_imports/bin/managing_imports.dart deleted file mode 100644 index 8405dd9..0000000 --- a/examples/managing_imports/bin/managing_imports.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:managing_imports/functional.dart'; -import 'package:test/test.dart'; - -void main() { - /// Borrow the `flatMap` test from `state_test.dart` - test('flatMap', () { - final state = FpState<List<int>, int>((s) => (s.first, s.sublist(1))); - final ap = state.flatMap<double>( - (a) => FpState( - (s) => (a / 2, s.sublist(1)), - ), - ); - final result = ap.run([1, 2, 3, 4, 5]); - expect(result.$1, 0.5); - expect(result.$2, [3, 4, 5]); - }); -} diff --git a/examples/managing_imports/lib/functional.dart b/examples/managing_imports/lib/functional.dart deleted file mode 100644 index c5ba286..0000000 --- a/examples/managing_imports/lib/functional.dart +++ /dev/null @@ -1,13 +0,0 @@ -// ignore_for_file: dangling_library_doc_comments - -/// This file is used to export all the functional programming libraries used in -/// the project. It also exports the `FpState` type alias, which is used to -/// avoid conflicts with the `State` class from the Flutter SDK. - -import 'package:fpdart/fpdart.dart' as fpdart show State; - -// The `fpdart` library is used to create functional programming constructs. -export 'package:fpdart/fpdart.dart' hide State; - -/// A type alias for the `State` class from the `fpdart` library. -typedef FpState<S, A> = fpdart.State<S, A>; diff --git a/examples/managing_imports/pubspec.yaml b/examples/managing_imports/pubspec.yaml deleted file mode 100644 index ebbca2d..0000000 --- a/examples/managing_imports/pubspec.yaml +++ /dev/null @@ -1,16 +0,0 @@ -name: managing_imports -description: Demonstration of managing imports -version: 1.0.0 -publish_to: none - -environment: - sdk: ">=3.0.0 <4.0.0" - -dependencies: - fpdart: - path: ../../packages/fpdart - test: - very_good_analysis: - -dev_dependencies: - lints: diff --git a/examples/open_meteo_api/.gitignore b/examples/open_meteo_api/.gitignore deleted file mode 100644 index 570e1df..0000000 --- a/examples/open_meteo_api/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.dart_tool/ -.packages -pubspec.lock - -# Generated files -**.g.dart -.idea/ \ No newline at end of file diff --git a/examples/open_meteo_api/README.md b/examples/open_meteo_api/README.md deleted file mode 100644 index 1841225..0000000 --- a/examples/open_meteo_api/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Open Meteo API - `fpdart` -This is a re-implementation using `fpdart` and functional programming of the [Open Meteo API](https://github.com/felangel/bloc/tree/master/examples/flutter_weather/packages/open_meteo_api) from the [flutter_weather](https://bloclibrary.dev/#/flutterweathertutorial) app example in the [bloc](https://pub.dev/packages/bloc) package. - -The goal is to show a comparison between usual dart code and functional code written using `fpdart`. - -## Structure -The example is simple but comprehensive. - -The Open Meteo API implementation is only 1 file. The original source is [open_meteo_api_client.dart](./lib/src/open_meteo_api_client.dart) (copy of the [bloc package implementation](https://github.com/felangel/bloc/blob/master/examples/flutter_weather/packages/open_meteo_api/lib/src/open_meteo_api_client.dart)). - -Inside [lib/src/fpdart](./lib/src/fpdart/) you can then find the refactoring using functional programming and `fpdart`: -- [open_meteo_api_client_fpdart.dart](./lib/src/fpdart/open_meteo_api_client_fpdart.dart): implementation of the Open Meteo API with `fpdart` -- [location_failure.dart](./lib/src/fpdart/location_failure.dart): failure classes for the `locationSearch` request -- [weather_failure.dart](./lib/src/fpdart/weather_failure.dart): failure classes for the `getWeather` request - -### Test -Also the [test](./test/) has been rewritten based on the `fpdart` refactoring: -- [open_meteo_api_client_test.dart](./test/open_meteo_api_client_test.dart): Original Open Meteo API test implementation -- [open_meteo_api_client_test_fpdart.dart](./test/open_meteo_api_client_test_fpdart.dart): Testing for the new implementation using `fpdart` and functional programming - -## Types used from `fpdart` -- `TaskEither`: Used instead of `Future` to make async request that may fail -- `Either`: Used to validate the response from the API with either an error or a valid value -- `Option`: Used to get values that may be missing - - `lookup` in a `Map`: getting a value by key in a `Map` may return nothing if the key is not found - - `head` in a `List`: The list may be empty, so getting the first element (called _"head"_) may return nothing \ No newline at end of file diff --git a/examples/open_meteo_api/analysis_options.yaml b/examples/open_meteo_api/analysis_options.yaml deleted file mode 100644 index 1d5ab95..0000000 --- a/examples/open_meteo_api/analysis_options.yaml +++ /dev/null @@ -1,8 +0,0 @@ -include: package:very_good_analysis/analysis_options.3.0.2.yaml -analyzer: - exclude: - - lib/**/*.g.dart - -linter: - rules: - public_member_api_docs: false diff --git a/examples/open_meteo_api/build.yaml b/examples/open_meteo_api/build.yaml deleted file mode 100644 index 917f456..0000000 --- a/examples/open_meteo_api/build.yaml +++ /dev/null @@ -1,12 +0,0 @@ -targets: - $default: - builders: - source_gen|combining_builder: - options: - ignore_for_file: - - implicit_dynamic_parameter - json_serializable: - options: - field_rename: snake - create_to_json: false - checked: true diff --git a/examples/open_meteo_api/lib/open_meteo_api.dart b/examples/open_meteo_api/lib/open_meteo_api.dart deleted file mode 100644 index 90cc854..0000000 --- a/examples/open_meteo_api/lib/open_meteo_api.dart +++ /dev/null @@ -1,5 +0,0 @@ -library open_meteo_api; - -export 'src/fpdart/open_meteo_api_client_fpdart.dart'; -export 'src/models/models.dart'; -export 'src/open_meteo_api_client.dart'; diff --git a/examples/open_meteo_api/lib/src/fpdart/location_failure.dart b/examples/open_meteo_api/lib/src/fpdart/location_failure.dart deleted file mode 100644 index 911d772..0000000 --- a/examples/open_meteo_api/lib/src/fpdart/location_failure.dart +++ /dev/null @@ -1,65 +0,0 @@ -// ignore_for_file: comment_references - -import 'package:http/http.dart' as http; - -/// Abstract class which represents a failure in the `locationSearch` request. -abstract class OpenMeteoApiFpdartLocationFailure {} - -/// [OpenMeteoApiFpdartLocationFailure] when **http request** fails -class LocationHttpRequestFpdartFailure - implements OpenMeteoApiFpdartLocationFailure { - const LocationHttpRequestFpdartFailure(this.object, this.stackTrace); - final Object object; - final StackTrace stackTrace; -} - -/// [OpenMeteoApiFpdartLocationFailure] when request is not successful -/// (`status != 200`) -class LocationRequestFpdartFailure - implements OpenMeteoApiFpdartLocationFailure { - const LocationRequestFpdartFailure(this.response); - final http.Response response; -} - -/// [OpenMeteoApiFpdartLocationFailure] when location response -/// cannot be decoded from json. -class LocationInvalidJsonDecodeFpdartFailure - implements OpenMeteoApiFpdartLocationFailure { - const LocationInvalidJsonDecodeFpdartFailure(this.body); - final String body; -} - -/// [OpenMeteoApiFpdartLocationFailure] when location response is not a valid -/// [Map]. -class LocationInvalidMapFpdartFailure - implements OpenMeteoApiFpdartLocationFailure { - const LocationInvalidMapFpdartFailure(this.json); - final dynamic json; -} - -/// [OpenMeteoApiFpdartLocationFailure] when location information -/// is not found (missing expected key). -class LocationKeyNotFoundFpdartFailure - implements OpenMeteoApiFpdartLocationFailure {} - -/// [OpenMeteoApiFpdartLocationFailure] when location data is not a valid -/// [List]. -class LocationInvalidListFpdartFailure - implements OpenMeteoApiFpdartLocationFailure { - const LocationInvalidListFpdartFailure(this.value); - final dynamic value; -} - -/// [OpenMeteoApiFpdartLocationFailure] when location for provided location -/// is not found (missing data). -class LocationDataNotFoundFpdartFailure - implements OpenMeteoApiFpdartLocationFailure {} - -/// [OpenMeteoApiFpdartLocationFailure] when the response is not -/// a valid [Location] -class LocationFormattingFpdartFailure - implements OpenMeteoApiFpdartLocationFailure { - const LocationFormattingFpdartFailure(this.object, this.stackTrace); - final Object object; - final StackTrace stackTrace; -} diff --git a/examples/open_meteo_api/lib/src/fpdart/open_meteo_api_client_fpdart.dart b/examples/open_meteo_api/lib/src/fpdart/open_meteo_api_client_fpdart.dart deleted file mode 100644 index edc4b4a..0000000 --- a/examples/open_meteo_api/lib/src/fpdart/open_meteo_api_client_fpdart.dart +++ /dev/null @@ -1,141 +0,0 @@ -import 'dart:convert'; - -import 'package:fpdart/fpdart.dart'; -import 'package:http/http.dart' as http; -import 'package:open_meteo_api/open_meteo_api.dart'; -import 'package:open_meteo_api/src/fpdart/location_failure.dart'; -import 'package:open_meteo_api/src/fpdart/weather_failure.dart'; - -class OpenMeteoApiClientFpdart { - OpenMeteoApiClientFpdart({http.Client? httpClient}) - : _httpClient = httpClient ?? http.Client(); - - static const _baseUrlWeather = 'api.open-meteo.com'; - static const _baseUrlGeocoding = 'geocoding-api.open-meteo.com'; - - final http.Client _httpClient; - - /// Finds a [Location] `/v1/search/?name=(query)`. - TaskEither<OpenMeteoApiFpdartLocationFailure, Location> locationSearch( - String query, - ) => - TaskEither<OpenMeteoApiFpdartLocationFailure, http.Response>.tryCatch( - () => _httpClient.get( - Uri.https( - _baseUrlGeocoding, - '/v1/search', - {'name': query, 'count': '1'}, - ), - ), - LocationHttpRequestFpdartFailure.new, - ).chainEither( - (response) => Either.Do((_) { - final body = _( - _validResponseBody(response, LocationRequestFpdartFailure.new), - ); - - final json = _( - Either.tryCatch( - () => jsonDecode(body), - (_, __) => LocationInvalidJsonDecodeFpdartFailure(body), - ), - ); - - final data = _( - Either<OpenMeteoApiFpdartLocationFailure, - Map<dynamic, dynamic>>.safeCast( - json, - LocationInvalidMapFpdartFailure.new, - ), - ); - - final currentWeather = _( - data - .lookup('results') - .toEither(LocationKeyNotFoundFpdartFailure.new), - ); - - final results = _( - Either<OpenMeteoApiFpdartLocationFailure, List<dynamic>>.safeCast( - currentWeather, - LocationInvalidListFpdartFailure.new, - ), - ); - - final weather = _( - results.head.toEither(LocationDataNotFoundFpdartFailure.new), - ); - - return _( - Either.tryCatch( - () => Location.fromJson(weather as Map<String, dynamic>), - LocationFormattingFpdartFailure.new, - ), - ); - }), - ); - - /// Fetches [Weather] for a given [latitude] and [longitude]. - TaskEither<OpenMeteoApiFpdartWeatherFailure, Weather> getWeather({ - required double latitude, - required double longitude, - }) => - TaskEither<OpenMeteoApiFpdartWeatherFailure, http.Response>.tryCatch( - () async => _httpClient.get( - Uri.https( - _baseUrlWeather, - 'v1/forecast', - { - 'latitude': '$latitude', - 'longitude': '$longitude', - 'current_weather': 'true' - }, - ), - ), - WeatherHttpRequestFpdartFailure.new, - ) - .chainEither( - (response) => - _validResponseBody(response, WeatherRequestFpdartFailure.new), - ) - .chainEither( - (body) => Either.safeCastStrict< - OpenMeteoApiFpdartWeatherFailure, - Map<dynamic, dynamic>, - String>(body, WeatherInvalidMapFpdartFailure.new), - ) - .chainEither( - (body) => body - .lookup('current_weather') - .toEither(WeatherKeyNotFoundFpdartFailure.new), - ) - .chainEither( - (currentWeather) => Either<OpenMeteoApiFpdartWeatherFailure, - List<dynamic>>.safeCast( - currentWeather, - WeatherInvalidListFpdartFailure.new, - ), - ) - .chainEither( - (results) => - results.head.toEither(WeatherDataNotFoundFpdartFailure.new), - ) - .chainEither( - (weather) => Either.tryCatch( - () => Weather.fromJson(weather as Map<String, dynamic>), - WeatherFormattingFpdartFailure.new, - ), - ); - - /// Verify that the response status code is 200, - /// and extract the response's body. - Either<E, String> _validResponseBody<E>( - http.Response response, - E Function(http.Response) onError, - ) => - Either<E, http.Response>.fromPredicate( - response, - (r) => r.statusCode == 200, - onError, - ).map((r) => r.body); -} diff --git a/examples/open_meteo_api/lib/src/fpdart/weather_failure.dart b/examples/open_meteo_api/lib/src/fpdart/weather_failure.dart deleted file mode 100644 index 8d923bb..0000000 --- a/examples/open_meteo_api/lib/src/fpdart/weather_failure.dart +++ /dev/null @@ -1,54 +0,0 @@ -// ignore_for_file: comment_references - -import 'package:http/http.dart' as http; - -/// Abstract class which represents a failure in the `getWeather` request. -abstract class OpenMeteoApiFpdartWeatherFailure {} - -/// [OpenMeteoApiFpdartWeatherFailure] when **http request** fails -class WeatherHttpRequestFpdartFailure - implements OpenMeteoApiFpdartWeatherFailure { - const WeatherHttpRequestFpdartFailure(this.object, this.stackTrace); - final Object object; - final StackTrace stackTrace; -} - -/// [OpenMeteoApiFpdartWeatherFailure] when getWeather fails -class WeatherRequestFpdartFailure implements OpenMeteoApiFpdartWeatherFailure { - const WeatherRequestFpdartFailure(this.response); - final http.Response response; -} - -/// [OpenMeteoApiFpdartWeatherFailure] when weather response is not a valid -/// [Map]. -class WeatherInvalidMapFpdartFailure - implements OpenMeteoApiFpdartWeatherFailure { - const WeatherInvalidMapFpdartFailure(this.body); - final String body; -} - -/// [OpenMeteoApiFpdartWeatherFailure] when weather information -/// is not found (missing expected key). -class WeatherKeyNotFoundFpdartFailure - implements OpenMeteoApiFpdartWeatherFailure {} - -/// [OpenMeteoApiFpdartWeatherFailure] when weather data is not a valid [List]. -class WeatherInvalidListFpdartFailure - implements OpenMeteoApiFpdartWeatherFailure { - const WeatherInvalidListFpdartFailure(this.value); - final dynamic value; -} - -/// [OpenMeteoApiFpdartWeatherFailure] when weather for provided location -/// is not found (missing data). -class WeatherDataNotFoundFpdartFailure - implements OpenMeteoApiFpdartWeatherFailure {} - -/// [OpenMeteoApiFpdartWeatherFailure] when the response is not a valid -/// [Weather]. -class WeatherFormattingFpdartFailure - implements OpenMeteoApiFpdartWeatherFailure { - const WeatherFormattingFpdartFailure(this.object, this.stackTrace); - final Object object; - final StackTrace stackTrace; -} diff --git a/examples/open_meteo_api/lib/src/models/location.dart b/examples/open_meteo_api/lib/src/models/location.dart deleted file mode 100644 index f11c46c..0000000 --- a/examples/open_meteo_api/lib/src/models/location.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -part 'location.g.dart'; - -@JsonSerializable() -class Location { - const Location({ - required this.id, - required this.name, - required this.latitude, - required this.longitude, - }); - - factory Location.fromJson(Map<String, dynamic> json) => - _$LocationFromJson(json); - - final int id; - final String name; - final double latitude; - final double longitude; -} diff --git a/examples/open_meteo_api/lib/src/models/models.dart b/examples/open_meteo_api/lib/src/models/models.dart deleted file mode 100644 index 4f0d863..0000000 --- a/examples/open_meteo_api/lib/src/models/models.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'location.dart'; -export 'weather.dart'; diff --git a/examples/open_meteo_api/lib/src/models/weather.dart b/examples/open_meteo_api/lib/src/models/weather.dart deleted file mode 100644 index bb2fc68..0000000 --- a/examples/open_meteo_api/lib/src/models/weather.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:json_annotation/json_annotation.dart'; - -part 'weather.g.dart'; - -@JsonSerializable() -class Weather { - const Weather({required this.temperature, required this.weatherCode}); - - factory Weather.fromJson(Map<String, dynamic> json) => - _$WeatherFromJson(json); - - final double temperature; - @JsonKey(name: 'weathercode') - final double weatherCode; -} diff --git a/examples/open_meteo_api/lib/src/open_meteo_api_client.dart b/examples/open_meteo_api/lib/src/open_meteo_api_client.dart deleted file mode 100644 index 103eb51..0000000 --- a/examples/open_meteo_api/lib/src/open_meteo_api_client.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:http/http.dart' as http; -import 'package:open_meteo_api/open_meteo_api.dart'; - -/// Exception thrown when locationSearch fails. -class LocationRequestFailure implements Exception {} - -/// Exception thrown when the provided location is not found. -class LocationNotFoundFailure implements Exception {} - -/// Exception thrown when getWeather fails. -class WeatherRequestFailure implements Exception {} - -/// Exception thrown when weather for provided location is not found. -class WeatherNotFoundFailure implements Exception {} - -/// {@template open_meteo_api_client} -/// Dart API Client which wraps the [Open Meteo API](https://open-meteo.com). -/// {@endtemplate} -class OpenMeteoApiClient { - /// {@macro open_meteo_api_client} - OpenMeteoApiClient({http.Client? httpClient}) - : _httpClient = httpClient ?? http.Client(); - - static const _baseUrlWeather = 'api.open-meteo.com'; - static const _baseUrlGeocoding = 'geocoding-api.open-meteo.com'; - - final http.Client _httpClient; - - /// Finds a [Location] `/v1/search/?name=(query)`. - Future<Location> locationSearch(String query) async { - final locationRequest = Uri.https( - _baseUrlGeocoding, - '/v1/search', - {'name': query, 'count': '1'}, - ); - - final locationResponse = await _httpClient.get(locationRequest); - - if (locationResponse.statusCode != 200) { - throw LocationRequestFailure(); - } - - final locationJson = jsonDecode(locationResponse.body) as Map; - - if (!locationJson.containsKey('results')) throw LocationNotFoundFailure(); - - final results = locationJson['results'] as List; - - if (results.isEmpty) throw LocationNotFoundFailure(); - - return Location.fromJson(results.first as Map<String, dynamic>); - } - - /// Fetches [Weather] for a given [latitude] and [longitude]. - Future<Weather> getWeather({ - required double latitude, - required double longitude, - }) async { - final weatherRequest = Uri.https(_baseUrlWeather, 'v1/forecast', { - 'latitude': '$latitude', - 'longitude': '$longitude', - 'current_weather': 'true' - }); - - final weatherResponse = await _httpClient.get(weatherRequest); - - if (weatherResponse.statusCode != 200) { - throw WeatherRequestFailure(); - } - - final bodyJson = jsonDecode(weatherResponse.body) as Map<String, dynamic>; - - if (!bodyJson.containsKey('current_weather')) { - throw WeatherNotFoundFailure(); - } - - final weatherJson = bodyJson['current_weather'] as Map<String, dynamic>; - - return Weather.fromJson(weatherJson); - } -} diff --git a/examples/open_meteo_api/pubspec.yaml b/examples/open_meteo_api/pubspec.yaml deleted file mode 100644 index 0e976e8..0000000 --- a/examples/open_meteo_api/pubspec.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: open_meteo_api -description: A Dart API Client for the Open-Meteo API. -version: 1.0.0+1 -publish_to: "none" - -environment: - sdk: ">=2.18.0 <3.0.0" - -dependencies: - fpdart: - path: ../../packages/fpdart - - http: ^0.13.0 - json_annotation: ^4.6.0 - -dev_dependencies: - build_runner: ^2.0.0 - json_serializable: ^6.3.1 - mocktail: ^0.3.0 - test: ^1.16.4 - very_good_analysis: ^4.0.0+1 diff --git a/examples/open_meteo_api/test/open_meteo_api_client_test.dart b/examples/open_meteo_api/test/open_meteo_api_client_test.dart deleted file mode 100644 index 60a36aa..0000000 --- a/examples/open_meteo_api/test/open_meteo_api_client_test.dart +++ /dev/null @@ -1,202 +0,0 @@ -// ignore_for_file: prefer_const_constructors -import 'package:http/http.dart' as http; -import 'package:mocktail/mocktail.dart'; -import 'package:open_meteo_api/open_meteo_api.dart'; -import 'package:test/test.dart'; - -class MockHttpClient extends Mock implements http.Client {} - -class MockResponse extends Mock implements http.Response {} - -class FakeUri extends Fake implements Uri {} - -void main() { - group('OpenMeteoApiClient', () { - late http.Client httpClient; - late OpenMeteoApiClient apiClient; - - setUpAll(() { - registerFallbackValue(FakeUri()); - }); - - setUp(() { - httpClient = MockHttpClient(); - apiClient = OpenMeteoApiClient(httpClient: httpClient); - }); - - group('constructor', () { - test('does not require an httpClient', () { - expect(OpenMeteoApiClient(), isNotNull); - }); - }); - - group('locationSearch', () { - const query = 'mock-query'; - test('makes correct http request', () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn('{}'); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - try { - await apiClient.locationSearch(query); - } catch (_) {} - verify( - () => httpClient.get( - Uri.https( - 'geocoding-api.open-meteo.com', - '/v1/search', - {'name': query, 'count': '1'}, - ), - ), - ).called(1); - }); - - test('throws LocationRequestFailure on non-200 response', () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(400); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - expect( - () async => apiClient.locationSearch(query), - throwsA(isA<LocationRequestFailure>()), - ); - }); - - test('throws LocationNotFoundFailure on error response', () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn('{}'); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - await expectLater( - apiClient.locationSearch(query), - throwsA(isA<LocationNotFoundFailure>()), - ); - }); - - test('throws LocationNotFoundFailure on empty response', () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn('{"results": []}'); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - await expectLater( - apiClient.locationSearch(query), - throwsA(isA<LocationNotFoundFailure>()), - ); - }); - - test('returns Location on valid response', () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn( - ''' -{ - "results": [ - { - "id": 4887398, - "name": "Chicago", - "latitude": 41.85003, - "longitude": -87.65005 - } - ] -}''', - ); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - final actual = await apiClient.locationSearch(query); - expect( - actual, - isA<Location>() - .having((l) => l.name, 'name', 'Chicago') - .having((l) => l.id, 'id', 4887398) - .having((l) => l.latitude, 'latitude', 41.85003) - .having((l) => l.longitude, 'longitude', -87.65005), - ); - }); - }); - - group('getWeather', () { - const latitude = 41.85003; - const longitude = -87.6500; - - test('makes correct http request', () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn('{}'); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - try { - await apiClient.getWeather(latitude: latitude, longitude: longitude); - } catch (_) {} - verify( - () => httpClient.get( - Uri.https('api.open-meteo.com', 'v1/forecast', { - 'latitude': '$latitude', - 'longitude': '$longitude', - 'current_weather': 'true' - }), - ), - ).called(1); - }); - - test('throws WeatherRequestFailure on non-200 response', () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(400); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - expect( - () async => apiClient.getWeather( - latitude: latitude, - longitude: longitude, - ), - throwsA(isA<WeatherRequestFailure>()), - ); - }); - - test('throws WeatherNotFoundFailure on empty response', () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn('{}'); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - expect( - () async => apiClient.getWeather( - latitude: latitude, - longitude: longitude, - ), - throwsA(isA<WeatherNotFoundFailure>()), - ); - }); - - test('returns weather on valid response', () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn( - ''' -{ -"latitude": 43, -"longitude": -87.875, -"generationtime_ms": 0.2510547637939453, -"utc_offset_seconds": 0, -"timezone": "GMT", -"timezone_abbreviation": "GMT", -"elevation": 189, -"current_weather": { -"temperature": 15.3, -"windspeed": 25.8, -"winddirection": 310, -"weathercode": 63, -"time": "2022-09-12T01:00" -} -} - ''', - ); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - final actual = await apiClient.getWeather( - latitude: latitude, - longitude: longitude, - ); - expect( - actual, - isA<Weather>() - .having((w) => w.temperature, 'temperature', 15.3) - .having((w) => w.weatherCode, 'weatherCode', 63.0), - ); - }); - }); - }); -} diff --git a/examples/open_meteo_api/test/open_meteo_api_client_test_fpdart.dart b/examples/open_meteo_api/test/open_meteo_api_client_test_fpdart.dart deleted file mode 100644 index 1cc346e..0000000 --- a/examples/open_meteo_api/test/open_meteo_api_client_test_fpdart.dart +++ /dev/null @@ -1,249 +0,0 @@ -// ignore_for_file: prefer_const_constructors, lines_longer_than_80_chars -import 'package:fpdart/fpdart.dart'; -import 'package:http/http.dart' as http; -import 'package:mocktail/mocktail.dart'; -import 'package:open_meteo_api/open_meteo_api.dart'; -import 'package:open_meteo_api/src/fpdart/location_failure.dart'; -import 'package:test/test.dart'; - -class MockHttpClient extends Mock implements http.Client {} - -class MockResponse extends Mock implements http.Response {} - -class FakeUri extends Fake implements Uri {} - -void _isLeftOfType<E, R, T>( - Either<E, R> result, { - dynamic Function(TypeMatcher<T>)? typeMatch, -}) { - expect(result, isA<Left<E, R>>()); - result.match( - (l) => expect(l, typeMatch != null ? typeMatch(isA<T>()) : isA<T>()), - (_) => fail('should not be right'), - ); -} - -void main() { - group('OpenMeteoApiClientFpdart', () { - late http.Client httpClient; - late OpenMeteoApiClientFpdart apiClient; - - setUpAll(() { - registerFallbackValue(FakeUri()); - }); - - setUp(() { - httpClient = MockHttpClient(); - apiClient = OpenMeteoApiClientFpdart(httpClient: httpClient); - }); - - group('constructor', () { - test('does not require an httpClient', () { - expect(OpenMeteoApiClientFpdart(), isNotNull); - }); - }); - - group('locationSearch', () { - const query = 'mock-query'; - test('makes correct http request', () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn('{}'); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - - /// No need of try/catch - await apiClient.locationSearch(query).run(); - - verify( - () => httpClient.get( - Uri.https( - 'geocoding-api.open-meteo.com', - '/v1/search', - {'name': query, 'count': '1'}, - ), - ), - ).called(1); - }); - - test('returns LocationHttpRequestFpdartFailure when http request fails', - () async { - when(() => httpClient.get(any())).thenThrow(Exception()); - - final result = await apiClient.locationSearch(query).run(); - - _isLeftOfType<OpenMeteoApiFpdartLocationFailure, Location, - LocationHttpRequestFpdartFailure>( - result, - typeMatch: (m) => m.having( - (failure) => failure.object, - 'Exception', - isA<Exception>(), - ), - ); - }); - - test('returns LocationRequestFpdartFailure on non-200 response', - () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(400); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - - final result = await apiClient.locationSearch(query).run(); - - _isLeftOfType<OpenMeteoApiFpdartLocationFailure, Location, - LocationRequestFpdartFailure>( - result, - typeMatch: (m) => m.having( - (failure) => failure.response, - 'MockResponse', - response, - ), - ); - }); - - test( - 'returns LocationInvalidJsonDecodeFpdartFailure when response is invalid', - () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn('_{}_'); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - - final result = await apiClient.locationSearch(query).run(); - - _isLeftOfType<OpenMeteoApiFpdartLocationFailure, Location, - LocationInvalidJsonDecodeFpdartFailure>( - result, - typeMatch: (m) => m.having( - (failure) => failure.body, - 'body', - '_{}_', - ), - ); - }); - - test('returns LocationInvalidMapFpdartFailure when response is not a Map', - () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn('[]'); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - - final result = await apiClient.locationSearch(query).run(); - - _isLeftOfType<OpenMeteoApiFpdartLocationFailure, Location, - LocationInvalidMapFpdartFailure>( - result, - typeMatch: (m) => m.having( - (failure) => failure.json, - 'json', - [], - ), - ); - }); - - test( - 'returns LocationKeyNotFoundFpdartFailure when the response is missing the correct key', - () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn('{}'); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - - final result = await apiClient.locationSearch(query).run(); - - _isLeftOfType<OpenMeteoApiFpdartLocationFailure, Location, - LocationKeyNotFoundFpdartFailure>(result); - }); - - test( - 'returns LocationInvalidListFpdartFailure when Map key does not contain a valid List', - () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn('{"results": {}}'); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - - final result = await apiClient.locationSearch(query).run(); - - _isLeftOfType<OpenMeteoApiFpdartLocationFailure, Location, - LocationInvalidListFpdartFailure>( - result, - typeMatch: (m) => m.having( - (failure) => failure.value, - 'value', - {}, - ), - ); - }); - - test('returns LocationDataNotFoundFpdartFailure on empty response', - () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn('{"results": []}'); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - - final result = await apiClient.locationSearch(query).run(); - - _isLeftOfType<OpenMeteoApiFpdartLocationFailure, Location, - LocationDataNotFoundFpdartFailure>(result); - }); - - test( - 'returns LocationFormattingFpdartFailure when response is not a correct Location object', - () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn( - ''' -{ - "results": [ - { - "_id": 4887398, - "_name": "Chicago", - "_latitude": 41.85003, - "_longitude": -87.65005 - } - ] -}''', - ); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - - final result = await apiClient.locationSearch(query).run(); - - _isLeftOfType<OpenMeteoApiFpdartLocationFailure, Location, - LocationFormattingFpdartFailure>(result); - }); - - test('returns Location on valid response', () async { - final response = MockResponse(); - when(() => response.statusCode).thenReturn(200); - when(() => response.body).thenReturn( - ''' -{ - "results": [ - { - "id": 4887398, - "name": "Chicago", - "latitude": 41.85003, - "longitude": -87.65005 - } - ] -}''', - ); - when(() => httpClient.get(any())).thenAnswer((_) async => response); - - final result = await apiClient.locationSearch(query).run(); - expect( - result, - isA<Right<OpenMeteoApiFpdartLocationFailure, Location>>() - .having((l) => l.value.name, 'name', 'Chicago') - .having((l) => l.value.id, 'id', 4887398) - .having((l) => l.value.latitude, 'latitude', 41.85003) - .having((l) => l.value.longitude, 'longitude', -87.65005), - ); - }); - }); - }); -} diff --git a/examples/pokeapi_functional/.gitattributes b/examples/pokeapi_functional/.gitattributes deleted file mode 100644 index 243a769..0000000 --- a/examples/pokeapi_functional/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -*.freezed.dart linguist-generated=true -*.g.dart linguist-generated=true diff --git a/examples/pokeapi_functional/.gitignore b/examples/pokeapi_functional/.gitignore deleted file mode 100644 index d207f7e..0000000 --- a/examples/pokeapi_functional/.gitignore +++ /dev/null @@ -1,48 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.metadata -.pub-cache/ -.pub/ -/build/ -pubspec.lock - -# Web related -lib/generated_plugin_registrant.dart - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release diff --git a/examples/pokeapi_functional/README.md b/examples/pokeapi_functional/README.md deleted file mode 100644 index 415b503..0000000 --- a/examples/pokeapi_functional/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# pokeapi_functional - -A new Flutter project. - -## Getting Started - -- `flutter pub get` -- `dart run build_runner build` -- `flutter run -d chrome` diff --git a/examples/pokeapi_functional/android/.gitignore b/examples/pokeapi_functional/android/.gitignore deleted file mode 100644 index 0a741cb..0000000 --- a/examples/pokeapi_functional/android/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties diff --git a/examples/pokeapi_functional/android/app/build.gradle b/examples/pokeapi_functional/android/app/build.gradle deleted file mode 100644 index 099ad9d..0000000 --- a/examples/pokeapi_functional/android/app/build.gradle +++ /dev/null @@ -1,59 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion 30 - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.sandromaglione.fpdart.pokeapi.pokeapi_functional" - minSdkVersion 16 - targetSdkVersion 30 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/examples/pokeapi_functional/android/app/src/debug/AndroidManifest.xml b/examples/pokeapi_functional/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 79a815e..0000000 --- a/examples/pokeapi_functional/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.sandromaglione.fpdart.pokeapi.pokeapi_functional"> - <!-- Flutter needs it to communicate with the running application - to allow setting breakpoints, to provide hot reload, etc. - --> - <uses-permission android:name="android.permission.INTERNET"/> -</manifest> diff --git a/examples/pokeapi_functional/android/app/src/main/AndroidManifest.xml b/examples/pokeapi_functional/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index a9ac11d..0000000 --- a/examples/pokeapi_functional/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,41 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.sandromaglione.fpdart.pokeapi.pokeapi_functional"> - <application - android:label="pokeapi_functional" - android:icon="@mipmap/ic_launcher"> - <activity - android:name=".MainActivity" - android:launchMode="singleTop" - android:theme="@style/LaunchTheme" - android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" - android:hardwareAccelerated="true" - android:windowSoftInputMode="adjustResize"> - <!-- Specifies an Android theme to apply to this Activity as soon as - the Android process has started. This theme is visible to the user - while the Flutter UI initializes. After that, this theme continues - to determine the Window background behind the Flutter UI. --> - <meta-data - android:name="io.flutter.embedding.android.NormalTheme" - android:resource="@style/NormalTheme" - /> - <!-- Displays an Android View that continues showing the launch screen - Drawable until Flutter paints its first frame, then this splash - screen fades out. A splash screen is useful to avoid any visual - gap between the end of Android's launch screen and the painting of - Flutter's first frame. --> - <meta-data - android:name="io.flutter.embedding.android.SplashScreenDrawable" - android:resource="@drawable/launch_background" - /> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER"/> - </intent-filter> - </activity> - <!-- Don't delete the meta-data below. - This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> - <meta-data - android:name="flutterEmbedding" - android:value="2" /> - </application> -</manifest> diff --git a/examples/pokeapi_functional/android/app/src/main/kotlin/com/sandromaglione/fpdart/pokeapi/pokeapi_functional/MainActivity.kt b/examples/pokeapi_functional/android/app/src/main/kotlin/com/sandromaglione/fpdart/pokeapi/pokeapi_functional/MainActivity.kt deleted file mode 100644 index e409e61..0000000 --- a/examples/pokeapi_functional/android/app/src/main/kotlin/com/sandromaglione/fpdart/pokeapi/pokeapi_functional/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.sandromaglione.fpdart.pokeapi.pokeapi_functional - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/examples/pokeapi_functional/android/app/src/main/res/drawable-v21/launch_background.xml b/examples/pokeapi_functional/android/app/src/main/res/drawable-v21/launch_background.xml deleted file mode 100644 index f74085f..0000000 --- a/examples/pokeapi_functional/android/app/src/main/res/drawable-v21/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Modify this file to customize your launch splash screen --> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="?android:colorBackground" /> - - <!-- You can insert your own image assets here --> - <!-- <item> - <bitmap - android:gravity="center" - android:src="@mipmap/launch_image" /> - </item> --> -</layer-list> diff --git a/examples/pokeapi_functional/android/app/src/main/res/drawable/launch_background.xml b/examples/pokeapi_functional/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f..0000000 --- a/examples/pokeapi_functional/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Modify this file to customize your launch splash screen --> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@android:color/white" /> - - <!-- You can insert your own image assets here --> - <!-- <item> - <bitmap - android:gravity="center" - android:src="@mipmap/launch_image" /> - </item> --> -</layer-list> diff --git a/examples/pokeapi_functional/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/pokeapi_functional/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U<k`UN+s5qQ#T?KrmN7v*f2L%`GO4-^ z(#_kX+jhyc?*UrsvLED<w+p3gfi4y<3GxeO5CDRJfCLa|I1d8%B?X@R0!qL1ba4!c zIQ;g^b-pGC0f)elH+vJ_clF3>|N3vA*22N<t#oc3|FNCx%`Ll}Jbl*Q`}yg~1ZO@= zF!6p)NTYsh!6(JdtLiRuwi@`&XeqAXe9fY|=kfFy_3t|md##(iHE+K4ydxH3>aGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#i<K@6w~yiZZH!59hqLTYF-H zp0%uz(2~z(X$>qdw@AL`7MR}m`rwr|mZgU`8P7SB<Kba6`>kL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZ<W8RzO1)p=v7f>vCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/examples/pokeapi_functional/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/examples/pokeapi_functional/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q0<?|W-^A&VhjhO+044$%!YxiPI;`p4Q#=k za-F;6dk-j1J*_nBlG2>3KywUtLX8Ua?`H+NMzkczFPK3<KtaL4z@UHr{Pp|KpFjT| z1oAia3j-A_^>lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zyt<ns@%x_-ezPmiS-=02Ut8`WUGa5ad?jb?UB<rkVmAJ)*Xy}nR&U>Q?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@U<OZDKbLh*2~7avPrJzg diff --git a/examples/pokeapi_functional/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/examples/pokeapi_functional/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 09d4391482be68e9e4a07fab769b5de337d16eb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 721 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy!iOI#yLg7ec#$`gxH85~pclTsBt za}(23gHjVyDhp4h+5i=O3-AeX1=1l$e`s#|#^}+&7(N@w0CIr{$Oe+Uk^K-ZP~83C zcc@hG6rikF&NPT(23>y!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg z<JnHTOuK%sGbNji)8cv6*ZNtnbwxJZ-?eY1e-ZPG#r<}_kct2Ie>oq1^2_p9@|WEo z*X_Uko@K)qYYv~<poG`LOv(2(Hhkk{Dz;b6_#4<(=XBwh@>>43eQGMdbiGbo>E~Q& zr<n4_y=h^-h|%}kO&jwCioQ5EzF6K8^VdP)H>YBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}f<lyK#S=nvDw%*y%vwN3psIS+1s7G}QfbZGd| zV;VoMy*YI2-%hU{-w$hl9!*Y_m~60h&vVVs6)lfgu09v4QWIt25UBGxaN`?mVmn*e U?z!!Jz`$qlboFyt=akR{0C)T?>Hq)$ diff --git a/examples/pokeapi_functional/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/pokeapi_functional/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*<I`!eYGGw1~|=m#_ChcFm~F&M`%SZ6TU=QA3HGn&RTnk6urCox*3Gg@ad z+GI1@<S^O-Q7)rh9;1CBqhm3nV+o^EDWh`*qe~T|Yc-==4WnBvqgx%LdmW=k1EWVH zqbCqGF?u#LdbTinH8Xm(F#5DJ`gAb*0#PTUUpJ#)52HU2^)d$ZF$VTC22NlMoX8kB zi7{v*V-S#>%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&f<tNc(Fvx&VC63f@(X4V5O7Fnm=6N`&!0cPZsO%v z3=B;Ao-U3d6}R4ANDmGU6lr~^!Nn0K;_9?HfJbVIkW)*G%Yg&F)*BmaZ)6Dl_&?vn za8gV0`@6<5-|YBa|J|21FVarliS1gU$698&iU*DCL=pdt&mR>lvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2<a5l9V~;L&ZJD?F zD6?3<TgqciA=l+!=Lp=a%2;%{dX@{HT-OP1|K<}HAM1TO7OY<MaYyH#Pk)4WR?Ts} zSNX4tLr#6-^m$z!Pd18IzSivQkh7k6T`o7OKThT4#m*F4b=OPt(kAX%9+=X!JT9f} zc|=Oray6sl!J;#tXm&@~>^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b z<q`EIyVgP^b`|@{uDX5oB6GTR%^Or)?eBf<KkJg@eR6BbGYJ*-U;nQCudos6R9mG` zc=6fn-^`2s)0Hm%cpbX>pQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9<SN`RwE}r7KC~Q%?|CMD5O&m%cKw{Bn6{qJbmFIooO_;g=+H+v4V(@hJb6Mw< G&;$S>I;xof diff --git a/examples/pokeapi_functional/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/examples/pokeapi_functional/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`Y<EGuRA6NX2Md zPqmGQbJPjRLrI#9?bL25ZHra>V6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_M<hJUv`}aj#i9>sH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8<s&Q-yp4r*9 z(t|8L&`vhm$wAg!MByQdFQW1h)eqSS&@P++v^xmx2}OHF$WDZ4QHUN37S3Sd0#=@| zD*$YR!8QbJ55gWH>=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~<G>Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%<BT_6>4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8h<N~B$l%}NU6KvLZj~&>t^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z<NEXJGYh+`9LE^Z1#g~OZ)yl?O>%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z<YzdcoCZ;A^sD<`wcO1=DLw>~7YxD~Rf<<Lt&2H?f`^~9TW?0O(WnyXeZvF- hHxv6WEXUT@T$j&O|7sZ6iSVC<kf$5l^{TT-@h=>(a@_y` diff --git a/examples/pokeapi_functional/android/app/src/main/res/values-night/styles.xml b/examples/pokeapi_functional/android/app/src/main/res/values-night/styles.xml deleted file mode 100644 index 449a9f9..0000000 --- a/examples/pokeapi_functional/android/app/src/main/res/values-night/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> - <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> - <!-- Show a splash screen on the activity. Automatically removed when - Flutter draws its first frame --> - <item name="android:windowBackground">@drawable/launch_background</item> - </style> - <!-- Theme applied to the Android Window as soon as the process has started. - This theme determines the color of the Android Window while your - Flutter UI initializes, as well as behind your Flutter UI while its - running. - - This Theme is only used starting with V2 of Flutter's Android embedding. --> - <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> - <item name="android:windowBackground">?android:colorBackground</item> - </style> -</resources> diff --git a/examples/pokeapi_functional/android/app/src/main/res/values/styles.xml b/examples/pokeapi_functional/android/app/src/main/res/values/styles.xml deleted file mode 100644 index d74aa35..0000000 --- a/examples/pokeapi_functional/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> - <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> - <!-- Show a splash screen on the activity. Automatically removed when - Flutter draws its first frame --> - <item name="android:windowBackground">@drawable/launch_background</item> - </style> - <!-- Theme applied to the Android Window as soon as the process has started. - This theme determines the color of the Android Window while your - Flutter UI initializes, as well as behind your Flutter UI while its - running. - - This Theme is only used starting with V2 of Flutter's Android embedding. --> - <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> - <item name="android:windowBackground">?android:colorBackground</item> - </style> -</resources> diff --git a/examples/pokeapi_functional/android/app/src/profile/AndroidManifest.xml b/examples/pokeapi_functional/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 79a815e..0000000 --- a/examples/pokeapi_functional/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.sandromaglione.fpdart.pokeapi.pokeapi_functional"> - <!-- Flutter needs it to communicate with the running application - to allow setting breakpoints, to provide hot reload, etc. - --> - <uses-permission android:name="android.permission.INTERNET"/> -</manifest> diff --git a/examples/pokeapi_functional/android/build.gradle b/examples/pokeapi_functional/android/build.gradle deleted file mode 100644 index 9b6ed06..0000000 --- a/examples/pokeapi_functional/android/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -buildscript { - ext.kotlin_version = '1.3.50' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - jcenter() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/examples/pokeapi_functional/android/gradle.properties b/examples/pokeapi_functional/android/gradle.properties deleted file mode 100644 index 94adc3a..0000000 --- a/examples/pokeapi_functional/android/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true -android.enableJetifier=true diff --git a/examples/pokeapi_functional/android/gradle/wrapper/gradle-wrapper.properties b/examples/pokeapi_functional/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index bc6a58a..0000000 --- a/examples/pokeapi_functional/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Jun 23 08:50:38 CEST 2017 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/examples/pokeapi_functional/android/settings.gradle b/examples/pokeapi_functional/android/settings.gradle deleted file mode 100644 index 44e62bc..0000000 --- a/examples/pokeapi_functional/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/examples/pokeapi_functional/ios/.gitignore b/examples/pokeapi_functional/ios/.gitignore deleted file mode 100644 index 151026b..0000000 --- a/examples/pokeapi_functional/ios/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/examples/pokeapi_functional/ios/Flutter/AppFrameworkInfo.plist b/examples/pokeapi_functional/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 9367d48..0000000 --- a/examples/pokeapi_functional/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleDevelopmentRegion</key> - <string>en</string> - <key>CFBundleExecutable</key> - <string>App</string> - <key>CFBundleIdentifier</key> - <string>io.flutter.flutter.app</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleName</key> - <string>App</string> - <key>CFBundlePackageType</key> - <string>FMWK</string> - <key>CFBundleShortVersionString</key> - <string>1.0</string> - <key>CFBundleSignature</key> - <string>????</string> - <key>CFBundleVersion</key> - <string>1.0</string> - <key>MinimumOSVersion</key> - <string>8.0</string> -</dict> -</plist> diff --git a/examples/pokeapi_functional/ios/Flutter/Debug.xcconfig b/examples/pokeapi_functional/ios/Flutter/Debug.xcconfig deleted file mode 100644 index 592ceee..0000000 --- a/examples/pokeapi_functional/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/examples/pokeapi_functional/ios/Flutter/Release.xcconfig b/examples/pokeapi_functional/ios/Flutter/Release.xcconfig deleted file mode 100644 index 592ceee..0000000 --- a/examples/pokeapi_functional/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/examples/pokeapi_functional/ios/Runner.xcodeproj/project.pbxproj b/examples/pokeapi_functional/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 7e08de3..0000000 --- a/examples/pokeapi_functional/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,471 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = "<group>"; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - ); - sourceTree = "<group>"; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = "<group>"; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = "<group>"; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1020; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = "<group>"; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = "<group>"; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.sandromaglione.fpdart.pokeapi.pokeapiFunctional; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.sandromaglione.fpdart.pokeapi.pokeapiFunctional; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.sandromaglione.fpdart.pokeapi.pokeapiFunctional; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Workspace - version = "1.0"> - <FileRef - location = "self:"> - </FileRef> -</Workspace> diff --git a/examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>IDEDidComputeMac32BitWarning</key> - <true/> -</dict> -</plist> diff --git a/examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/examples/pokeapi_functional/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>PreviewsEnabled</key> - <false/> -</dict> -</plist> diff --git a/examples/pokeapi_functional/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/examples/pokeapi_functional/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index a28140c..0000000 --- a/examples/pokeapi_functional/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Scheme - LastUpgradeVersion = "1020" - version = "1.3"> - <BuildAction - parallelizeBuildables = "YES" - buildImplicitDependencies = "YES"> - <BuildActionEntries> - <BuildActionEntry - buildForTesting = "YES" - buildForRunning = "YES" - buildForProfiling = "YES" - buildForArchiving = "YES" - buildForAnalyzing = "YES"> - <BuildableReference - BuildableIdentifier = "primary" - BlueprintIdentifier = "97C146ED1CF9000F007C117D" - BuildableName = "Runner.app" - BlueprintName = "Runner" - ReferencedContainer = "container:Runner.xcodeproj"> - </BuildableReference> - </BuildActionEntry> - </BuildActionEntries> - </BuildAction> - <TestAction - buildConfiguration = "Debug" - selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" - selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> - <Testables> - </Testables> - <MacroExpansion> - <BuildableReference - BuildableIdentifier = "primary" - BlueprintIdentifier = "97C146ED1CF9000F007C117D" - BuildableName = "Runner.app" - BlueprintName = "Runner" - ReferencedContainer = "container:Runner.xcodeproj"> - </BuildableReference> - </MacroExpansion> - <AdditionalOptions> - </AdditionalOptions> - </TestAction> - <LaunchAction - buildConfiguration = "Debug" - selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" - selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - launchStyle = "0" - useCustomWorkingDirectory = "NO" - ignoresPersistentStateOnLaunch = "NO" - debugDocumentVersioning = "YES" - debugServiceExtension = "internal" - allowLocationSimulation = "YES"> - <BuildableProductRunnable - runnableDebuggingMode = "0"> - <BuildableReference - BuildableIdentifier = "primary" - BlueprintIdentifier = "97C146ED1CF9000F007C117D" - BuildableName = "Runner.app" - BlueprintName = "Runner" - ReferencedContainer = "container:Runner.xcodeproj"> - </BuildableReference> - </BuildableProductRunnable> - <AdditionalOptions> - </AdditionalOptions> - </LaunchAction> - <ProfileAction - buildConfiguration = "Profile" - shouldUseLaunchSchemeArgsEnv = "YES" - savedToolIdentifier = "" - useCustomWorkingDirectory = "NO" - debugDocumentVersioning = "YES"> - <BuildableProductRunnable - runnableDebuggingMode = "0"> - <BuildableReference - BuildableIdentifier = "primary" - BlueprintIdentifier = "97C146ED1CF9000F007C117D" - BuildableName = "Runner.app" - BlueprintName = "Runner" - ReferencedContainer = "container:Runner.xcodeproj"> - </BuildableReference> - </BuildableProductRunnable> - </ProfileAction> - <AnalyzeAction - buildConfiguration = "Debug"> - </AnalyzeAction> - <ArchiveAction - buildConfiguration = "Release" - revealArchiveInOrganizer = "YES"> - </ArchiveAction> -</Scheme> diff --git a/examples/pokeapi_functional/ios/Runner.xcworkspace/contents.xcworkspacedata b/examples/pokeapi_functional/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a1..0000000 --- a/examples/pokeapi_functional/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Workspace - version = "1.0"> - <FileRef - location = "group:Runner.xcodeproj"> - </FileRef> -</Workspace> diff --git a/examples/pokeapi_functional/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/examples/pokeapi_functional/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/examples/pokeapi_functional/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>IDEDidComputeMac32BitWarning</key> - <true/> -</dict> -</plist> diff --git a/examples/pokeapi_functional/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/examples/pokeapi_functional/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/examples/pokeapi_functional/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>PreviewsEnabled</key> - <false/> -</dict> -</plist> diff --git a/examples/pokeapi_functional/ios/Runner/AppDelegate.swift b/examples/pokeapi_functional/ios/Runner/AppDelegate.swift deleted file mode 100644 index 70693e4..0000000 --- a/examples/pokeapi_functional/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fa..0000000 --- a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725e9b0ddb1deab583e5b5102493aa332..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10932 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 diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png deleted file mode 100644 index 28c6bf03016f6c994b70f38d1b7346e5831b531f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 564 zcmV-40?Yl0P)<h;3K|Lk000e1NJLTq000yK000yS1^@s6jfou%00001b5ch_0Itp) z=>Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9<WQhydS~rpwVfTyjm}~vs+Nj zL2;<v%s@b+9jXc@S}#x@@VwF4twVwRxFCih^tWg?H)v?24ZSeYC3aaOsM8yWc7GEz z7zKYq3)^d7srCATzmw!h1d8POV>Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV<y2`lV{mrphLz zZs5NcIN~Wp-YgL)o*yksN!%A$BK4ovtf60)eD^OF((VmRH^fQ+0000<MNUMnLSTXr Cg!*^@ diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 2ccbfd967d9697cd4b83225558af2911e9571c9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1283 zcmV+e1^oJnP)<h;3K|Lk000e1NJLTq001Ze001Zm1^@s6jQ+T7000EaNkl<Zc$~$U zO>7ip7{`C_J2Tx<S}3NqpxB^%g)f1Fg2os<-~kDV#FGRs+&p_AidZnjqZ&0H2nR1* zj0q8l5fTo51w)j?(&7h*nm|&p(vR)ByR$QY4>Pmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?D<dJ!G*SVsfpkGU(q>uHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ z<Ez>X$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&<?Pa8X$2 z7!Cm2JRI`|2MxYb7DBni>Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=<hAE@CMcU{$tA%YXI_aT>O zx-7$a;U><*5L^!%<hgO?EU&+RjyvO7JjW1(jTP<<mHfpWO->xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#1<DGLlIe;R_chCpIPcPAH*zr@Cs z?fB^|EuMpG4X$HBVo_%sSAM_4?&DuFlF8#(Lxtca1NV%iv%qemMnJ<G_#|w%M>6c{ zJImlNL(np<oN+CTXIZs=Ip^Qn&FbY#Fn}>L!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bca859a3f474b03065bef75ba58a9e4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 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 diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cde12118dda48d71e01fcb589a74d069c5d7cb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1025 zcmV+c1pfPpP)<h;3K|Lk000e1NJLTq0012T0012b1^@s6R+DEB000BXNkl<Zc$~GD zO=w(I6vuz}zV|Yjbed17ZK+8~s#O|MP&clmqDBgWZmLq<SX}r4y4TvKx^iJf5Gq-y zOGQEjOCyP3MX4X5l~PNgieUPsktUPGBs1^bzl-}OnIxT=SE~o^V(z=|{La^$dmfUE zBm*GnK}km>U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$<lg6i9 zl}<@|7r;n*02l>gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~c<Gji=2TnCP5=vx8A($>TNYR~@Y9Kep<iQi*1HiJn2AMS!E&HK zVnfa>MPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF<H6foEqi-Iie=wvW~+c5O@{fz z15Zzj&L+qQPza&y;JLn#y}co!t)163;kCwx>4|Rt=<qKwceYzzPl3~A@%(s+$wlbO zc~D^BY8jsH33#eIY*@)P+hFCz&o3t2+iBU*?19gIU*g-zge@(Wn_CP4Vk(AbwubEK z374wAp49EV)x|L}FJ3uW;>2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1<yk<F_A9G0?W)jg&H7#tqwGWPN2m*&Q-?N5mfsA}|J<Z(-)UuQ+*R zjGb?c@Yn1jfi)zqX5f~c$$GU_a(w7qiMP%R9Utb>kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX<i!R0Z{NtsYrELo(c*J#)_6Q;B#i<)$v7hkaB2AVeLT(w zU;Ip%kGwHqz-)1z`*v*RqZjUHU2}-Rka8<C(s3i{5#T7-ISo1qYYdC!WW~O<hQAkM zTAD(la4CD$)8M0iM>?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7edb86cdfe0d15b4b0d98334a86163658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?;<Go5WWQn|6+uH2_m2r5lB}%5TZuPR`V-CQi_n2Vq{<`GLVi8qN@j# zqI`={L3C8G3I^(@3RHBhE;&c<RtGw!0)4Fxo!p8}d#c|cHc-(hHf$6dbHN{)B&J-6 z8CPP?l~^=?vruV^WG`2|w?$&vBC(2KTgB8_sZ6T`6J3jmsl%uYPl@SJV)#nyIF2<> zX%qdxCXQp<sXX*V>dKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2B<k6JOOEKm0v@7`o2xzafNf z3ZX<HM12iqsu+Xris0NgGTIwip-5ILf*5aQWp8EWDyC!=Q!?hXjP-1SH7#S$$T+=I zoW3be|GWDB>H1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4<euq@26~q7NegFU@k$`jdb$`xyeJ4P8+-Tj3Ra{hbRCmT! zBMV($^{^e=(UH`dj`r@`Y(H|el`alrd8skZwkzYhRr(Gz(QZp(Qd(hE^@5E~qU`i; z(SkYe?}g2Y&YJj296Moad2)yG0fTMR6U}BWMARc(<#Fztb!h_|mPqTik`s(WVh&f0 z?pxZ)BEP>{|44tmgH^2hSzPFd;U^!1p>6d|o)<M!d`#7E^t}^Xk~3cs-?nsZx|T{j zmy5ZGo@mfoIbG>(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6Me<Ad__=MhyIK(Twff~+;!n_S-82~DqfzFOPkl&Gp}(|cUQyE@haMbYi+pu2R#52 zQ(?79{^}u<pnJs07X?Jd1xEs{hF)^P@+6Hdd>cwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|<EP6d-<IdS!f=-N7pvgRfUm7gq{u46xo{ z(betbRXvB#X1rgdf*#8+ETp0)ap9@+(t?IGTzEX?@U7?cr16G3E(5=(NmSno5Zt_R J4_(fr{s-Hn_EP`= diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png deleted file mode 100644 index dcdc2306c28505ebc0b6c3a359c4d252bf626b9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1920 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 GLSTY{`iRf~ diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png deleted file mode 100644 index 2ccbfd967d9697cd4b83225558af2911e9571c9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1283 zcmV+e1^oJnP)<h;3K|Lk000e1NJLTq001Ze001Zm1^@s6jQ+T7000EaNkl<Zc$~$U zO>7ip7{`C_J2Tx<S}3NqpxB^%g)f1Fg2os<-~kDV#FGRs+&p_AidZnjqZ&0H2nR1* zj0q8l5fTo51w)j?(&7h*nm|&p(vR)ByR$QY4>Pmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?D<dJ!G*SVsfpkGU(q>uHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ z<Ez>X$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&<?Pa8X$2 z7!Cm2JRI`|2MxYb7DBni>Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=<hAE@CMcU{$tA%YXI_aT>O zx-7$a;U><*5L^!%<hgO?EU&+RjyvO7JjW1(jTP<<mHfpWO->xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#1<DGLlIe;R_chCpIPcPAH*zr@Cs z?fB^|EuMpG4X$HBVo_%sSAM_4?&DuFlF8#(Lxtca1NV%iv%qemMnJ<G_#|w%M>6c{ zJImlNL(np<oN+CTXIZs=Ip^Qn&FbY#Fn}>L!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5cee1c98386d13b17e89f719e83555b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1895 zcmV-t2blPYP)<h;3K|Lk000e1NJLTq002+`002-31^@s6juG;$000LrNkl<Zc-rlq zYitx%6vt<Fw^2VB4T?q+zmymXf>FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z<NnTMTRum1*%+mnSjZ82NM??Vg}70k3Gg^+l+nxV7}(C7198_R72dI-5UlDBz*px2 zu%;(D(S1I}{rkOg<fJb}fMnOMa3aAU6HtC$ok!svxfX(wgFYzi^ucUYQ4*ScK+&}J zo3OoKG9r}ec=!YX_%^TM8wKx>4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3Q<UC>U4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<<WTG=Nry ztr05|WS#(8l`v!{I?yWT$&C9h49jqFBzg6s`n)o9Uk|~MUpDMbNf}_sqA`#D8pMs2 zrvp%opvw*`ucud~hm_YLs~VOZ_rq1696qjX04cl!R~2}qLzRckg|`CX&FW14O(G(z z8HmH*M0o!OWJ8C9v;dNh7pa}9Fez+Vc-3jb`%i^u<Vvr!LwN6?6#fS(7&G7%rU<Vp zhVzVGCTB<zX&aEjV^boCdf^cTL+Lrs3V4Q|#w3E6%$P?yy5bby&NJmiIvzwl@aS?U zKW@Bx(jNv;#W_4vF6Fck^@J_$U;yF0cGL$CH+d2E%`l8Ij^UX$nb0<<KqTNQyOM`D zx%CE&-+cpScIby_2Ru`!v#1k`I`zYQNQF0Hk0QMg?Sg0eglh9&670O38a%3k1Q6|n zXX=!`EXSV+pb0~P=Z_07;>10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKu<Itw z>bPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{<j2UcX(kE!kd^R zya_EM@c2&vV>kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`<I#G$Xurfc(wp!98as2qkMltgZ()%2l}u#8P`9+P1`355x$(uY15fb14XOJ_E6$ z4#dihAXaTchz$xv8=YwDB6BT7c1#Sz!-SFFcm_OE-vD98G7w8vBdi((xFHNstg6>Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;Q<Td_I!-{QBiWeZV^>gjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 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 XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 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 XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3831 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 diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1888 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 aR{jt9+;fr}hV%pg0000<MNUMnLSTYU=Zg*i diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png deleted file mode 100644 index 6a84f41e14e27f4b11f16f9ee39279ac98f8d5ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3294 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 diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index d0e1f58536026aebc4f1f70e481f6993c9ff088d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3612 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 diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 0bedcf2..0000000 --- a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725..0000000 --- a/examples/pokeapi_functional/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/examples/pokeapi_functional/ios/Runner/Base.lproj/LaunchScreen.storyboard b/examples/pokeapi_functional/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c..0000000 --- a/examples/pokeapi_functional/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> - </dependencies> - <scenes> - <!--View Controller--> - <scene sceneID="EHf-IW-A2E"> - <objects> - <viewController id="01J-lp-oVM" sceneMemberID="viewController"> - <layoutGuides> - <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/> - <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/> - </layoutGuides> - <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"> - </imageView> - </subviews> - <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/> - <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/> - </constraints> - </view> - </viewController> - <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> - </objects> - <point key="canvasLocation" x="53" y="375"/> - </scene> - </scenes> - <resources> - <image name="LaunchImage" width="168" height="185"/> - </resources> -</document> diff --git a/examples/pokeapi_functional/ios/Runner/Base.lproj/Main.storyboard b/examples/pokeapi_functional/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c2851..0000000 --- a/examples/pokeapi_functional/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> - </dependencies> - <scenes> - <!--Flutter View Controller--> - <scene sceneID="tne-QT-ifu"> - <objects> - <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController"> - <layoutGuides> - <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> - <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> - </layoutGuides> - <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> - <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> - </view> - </viewController> - <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> - </objects> - </scene> - </scenes> -</document> diff --git a/examples/pokeapi_functional/ios/Runner/Info.plist b/examples/pokeapi_functional/ios/Runner/Info.plist deleted file mode 100644 index de2ddd8..0000000 --- a/examples/pokeapi_functional/ios/Runner/Info.plist +++ /dev/null @@ -1,45 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleDevelopmentRegion</key> - <string>$(DEVELOPMENT_LANGUAGE)</string> - <key>CFBundleExecutable</key> - <string>$(EXECUTABLE_NAME)</string> - <key>CFBundleIdentifier</key> - <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleName</key> - <string>pokeapi_functional</string> - <key>CFBundlePackageType</key> - <string>APPL</string> - <key>CFBundleShortVersionString</key> - <string>$(FLUTTER_BUILD_NAME)</string> - <key>CFBundleSignature</key> - <string>????</string> - <key>CFBundleVersion</key> - <string>$(FLUTTER_BUILD_NUMBER)</string> - <key>LSRequiresIPhoneOS</key> - <true/> - <key>UILaunchStoryboardName</key> - <string>LaunchScreen</string> - <key>UIMainStoryboardFile</key> - <string>Main</string> - <key>UISupportedInterfaceOrientations</key> - <array> - <string>UIInterfaceOrientationPortrait</string> - <string>UIInterfaceOrientationLandscapeLeft</string> - <string>UIInterfaceOrientationLandscapeRight</string> - </array> - <key>UISupportedInterfaceOrientations~ipad</key> - <array> - <string>UIInterfaceOrientationPortrait</string> - <string>UIInterfaceOrientationPortraitUpsideDown</string> - <string>UIInterfaceOrientationLandscapeLeft</string> - <string>UIInterfaceOrientationLandscapeRight</string> - </array> - <key>UIViewControllerBasedStatusBarAppearance</key> - <false/> -</dict> -</plist> diff --git a/examples/pokeapi_functional/ios/Runner/Runner-Bridging-Header.h b/examples/pokeapi_functional/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a5..0000000 --- a/examples/pokeapi_functional/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/examples/pokeapi_functional/lib/api/fetch_pokemon.dart b/examples/pokeapi_functional/lib/api/fetch_pokemon.dart deleted file mode 100644 index 392878c..0000000 --- a/examples/pokeapi_functional/lib/api/fetch_pokemon.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'dart:convert'; - -import 'package:fpdart/fpdart.dart'; -import 'package:http/http.dart' as http; -import 'package:pokeapi_functional/constants/constants.dart'; -import 'package:pokeapi_functional/models/pokemon.dart'; - -/// Parse [String] to [int] in a functional way using [IOEither]. -IOEither<String, int> _parseStringToInt(String str) => IOEither.tryCatch( - () => int.parse(str), - (_, __) => - 'Cannot convert input to valid pokemon id (it must be a number)!', - ); - -/// Validate the pokemon id inserted by the user: -/// 1. Parse [String] from the user to [int] -/// 2. Check pokemon id in valid range -/// -/// Chain (1) and (2) using `flatMap`. -IOEither<String, int> _validateUserPokemonId(String pokemonId) => - _parseStringToInt(pokemonId).flatMap( - (intPokemonId) => IOEither.fromPredicate( - intPokemonId, - (id) => - id >= Constants.minimumPokemonId && - id <= Constants.maximumPokemonId, - (id) => - 'Invalid pokemon id $id: the id must be between ${Constants.minimumPokemonId} and ${Constants.maximumPokemonId + 1}!', - ), - ); - -/// Make HTTP request to fetch pokemon information from the pokeAPI -/// using [TaskEither] to perform an async request in a composable way. -TaskEither<String, Pokemon> fetchPokemon(int pokemonId) => TaskEither.tryCatch( - () async { - final url = Uri.parse(Constants.requestAPIUrl(pokemonId)); - final response = await http.get(url); - return Pokemon.fromJson( - jsonDecode(response.body) as Map<String, dynamic>, - ); - }, - (error, __) => 'Unknown error: $error', - ); - -/// Try to parse the user input from [String] to [int] using [IOEither]. -/// We use [IOEither] since the `parse` method is **synchronous** (no need of [Future]). -/// -/// Then check that the pokemon id is in the valid range. -/// -/// If the validation is successful, then fetch the pokemon information from the [int] id. -/// -/// All the functions are simply chained together following the principle of composability. -TaskEither<String, Pokemon> fetchPokemonFromUserInput(String pokemonId) => - TaskEither.Do((_) async { - final validPokemonId = await _(_validateUserPokemonId( - pokemonId, - ).toTaskEither()); - return _(fetchPokemon(validPokemonId)); - }); - -TaskEither<String, Pokemon> fetchRandomPokemon = TaskEither.Do((_) async { - final pokemonId = await _(randomInt( - Constants.minimumPokemonId, - Constants.maximumPokemonId + 1, - ).toTaskEither()); - return _(fetchPokemon(pokemonId)); -}); diff --git a/examples/pokeapi_functional/lib/constants/constants.dart b/examples/pokeapi_functional/lib/constants/constants.dart deleted file mode 100644 index 84b301f..0000000 --- a/examples/pokeapi_functional/lib/constants/constants.dart +++ /dev/null @@ -1,7 +0,0 @@ -/// App constants under 'Constants' namespace. -abstract class Constants { - static const int minimumPokemonId = 1; - static const int maximumPokemonId = 898; - static String requestAPIUrl(int pokemonId) => - 'https://pokeapi.co/api/v2/pokemon/$pokemonId'; -} diff --git a/examples/pokeapi_functional/lib/controllers/pokemon_provider.dart b/examples/pokeapi_functional/lib/controllers/pokemon_provider.dart deleted file mode 100644 index cc65968..0000000 --- a/examples/pokeapi_functional/lib/controllers/pokemon_provider.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -import '../api/fetch_pokemon.dart'; -import '../models/pokemon.dart'; - -part 'pokemon_provider.g.dart'; - -@riverpod -class PokemonState extends _$PokemonState { - @override - FutureOr<Pokemon> build() async => - fetchRandomPokemon.getOrElse((l) => throw Exception(l)).run(); - - /// User request, try to convert user input to [int] and then - /// request the pokemon if successful. - Future<Unit> fetch(String pokemonId) async => _pokemonRequest( - () => fetchPokemonFromUserInput(pokemonId), - ); - - /// Generic private method to perform request and update the state. - Future<Unit> _pokemonRequest( - TaskEither<String, Pokemon> Function() request, - ) async { - state = AsyncLoading(); - final pokemon = request(); - state = (await pokemon.run()).match( - (error) => AsyncError(error, StackTrace.current), - (pokemon) => AsyncData(pokemon), - ); - return unit; - } -} diff --git a/examples/pokeapi_functional/lib/main.dart b/examples/pokeapi_functional/lib/main.dart deleted file mode 100644 index bc921f2..0000000 --- a/examples/pokeapi_functional/lib/main.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart' show useTextEditingController; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:pokeapi_functional/controllers/pokemon_provider.dart'; - -void main() { - /// [ProviderScope] required for riverpod state management - runApp(ProviderScope(child: MyApp())); -} - -class MyApp extends HookConsumerWidget { - @override - Widget build(BuildContext context, WidgetRef ref) { - /// [TextEditingController] using hooks - final controller = useTextEditingController(); - final requestStatus = ref.watch(pokemonStateProvider); - final pokemonNotifier = ref.watch(pokemonStateProvider.notifier); - - return MaterialApp( - title: 'Fpdart PokeAPI', - home: Scaffold( - body: Column( - children: [ - /// [TextField] and [ElevatedButton] to input pokemon id to fetch - TextField( - textInputAction: TextInputAction.next, - textAlign: TextAlign.center, - controller: controller, - decoration: InputDecoration( - hintText: 'Insert pokemon id number', - ), - ), - ElevatedButton( - onPressed: () => pokemonNotifier.fetch(controller.text), - child: Text('Get my pokemon!'), - ), - - /// Map each [AsyncValue] to a different UI - requestStatus.when( - loading: () => Center(child: CircularProgressIndicator()), - - /// When either is [Left], display error message 💥 - error: (error, stackTrace) => Text(error.toString()), - - /// When either is [Right], display pokemon 🤩 - data: (pokemon) => Card( - child: Column( - children: [ - Image.network( - pokemon.sprites.frontDefault, - width: 200, - height: 200, - ), - Padding( - padding: const EdgeInsets.only( - bottom: 24, - ), - child: Text( - pokemon.name, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 24, - ), - ), - ), - ], - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/examples/pokeapi_functional/lib/models/pokemon.dart b/examples/pokeapi_functional/lib/models/pokemon.dart deleted file mode 100644 index b3644c8..0000000 --- a/examples/pokeapi_functional/lib/models/pokemon.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:pokeapi_functional/models/sprite.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'pokemon.freezed.dart'; -part 'pokemon.g.dart'; - -/// Pokemon information, with method to deserialize json -@freezed -class Pokemon with _$Pokemon { - const factory Pokemon({ - required int id, - required String name, - required int height, - required int weight, - required Sprite sprites, - }) = _Pokemon; - - factory Pokemon.fromJson(Map<String, Object?> json) => - _$PokemonFromJson(json); -} diff --git a/examples/pokeapi_functional/lib/models/pokemon.freezed.dart b/examples/pokeapi_functional/lib/models/pokemon.freezed.dart deleted file mode 100644 index d7ab051..0000000 --- a/examples/pokeapi_functional/lib/models/pokemon.freezed.dart +++ /dev/null @@ -1,234 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'pokemon.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity<T>(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -Pokemon _$PokemonFromJson(Map<String, dynamic> json) { - return _Pokemon.fromJson(json); -} - -/// @nodoc -mixin _$Pokemon { - int get id => throw _privateConstructorUsedError; - String get name => throw _privateConstructorUsedError; - int get height => throw _privateConstructorUsedError; - int get weight => throw _privateConstructorUsedError; - Sprite get sprites => throw _privateConstructorUsedError; - - Map<String, dynamic> toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $PokemonCopyWith<Pokemon> get copyWith => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $PokemonCopyWith<$Res> { - factory $PokemonCopyWith(Pokemon value, $Res Function(Pokemon) then) = - _$PokemonCopyWithImpl<$Res, Pokemon>; - @useResult - $Res call({int id, String name, int height, int weight, Sprite sprites}); - - $SpriteCopyWith<$Res> get sprites; -} - -/// @nodoc -class _$PokemonCopyWithImpl<$Res, $Val extends Pokemon> - implements $PokemonCopyWith<$Res> { - _$PokemonCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? name = null, - Object? height = null, - Object? weight = null, - Object? sprites = null, - }) { - return _then(_value.copyWith( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - name: null == name - ? _value.name - : name // ignore: cast_nullable_to_non_nullable - as String, - height: null == height - ? _value.height - : height // ignore: cast_nullable_to_non_nullable - as int, - weight: null == weight - ? _value.weight - : weight // ignore: cast_nullable_to_non_nullable - as int, - sprites: null == sprites - ? _value.sprites - : sprites // ignore: cast_nullable_to_non_nullable - as Sprite, - ) as $Val); - } - - @override - @pragma('vm:prefer-inline') - $SpriteCopyWith<$Res> get sprites { - return $SpriteCopyWith<$Res>(_value.sprites, (value) { - return _then(_value.copyWith(sprites: value) as $Val); - }); - } -} - -/// @nodoc -abstract class _$$_PokemonCopyWith<$Res> implements $PokemonCopyWith<$Res> { - factory _$$_PokemonCopyWith( - _$_Pokemon value, $Res Function(_$_Pokemon) then) = - __$$_PokemonCopyWithImpl<$Res>; - @override - @useResult - $Res call({int id, String name, int height, int weight, Sprite sprites}); - - @override - $SpriteCopyWith<$Res> get sprites; -} - -/// @nodoc -class __$$_PokemonCopyWithImpl<$Res> - extends _$PokemonCopyWithImpl<$Res, _$_Pokemon> - implements _$$_PokemonCopyWith<$Res> { - __$$_PokemonCopyWithImpl(_$_Pokemon _value, $Res Function(_$_Pokemon) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? name = null, - Object? height = null, - Object? weight = null, - Object? sprites = null, - }) { - return _then(_$_Pokemon( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as int, - name: null == name - ? _value.name - : name // ignore: cast_nullable_to_non_nullable - as String, - height: null == height - ? _value.height - : height // ignore: cast_nullable_to_non_nullable - as int, - weight: null == weight - ? _value.weight - : weight // ignore: cast_nullable_to_non_nullable - as int, - sprites: null == sprites - ? _value.sprites - : sprites // ignore: cast_nullable_to_non_nullable - as Sprite, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_Pokemon implements _Pokemon { - const _$_Pokemon( - {required this.id, - required this.name, - required this.height, - required this.weight, - required this.sprites}); - - factory _$_Pokemon.fromJson(Map<String, dynamic> json) => - _$$_PokemonFromJson(json); - - @override - final int id; - @override - final String name; - @override - final int height; - @override - final int weight; - @override - final Sprite sprites; - - @override - String toString() { - return 'Pokemon(id: $id, name: $name, height: $height, weight: $weight, sprites: $sprites)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_Pokemon && - (identical(other.id, id) || other.id == id) && - (identical(other.name, name) || other.name == name) && - (identical(other.height, height) || other.height == height) && - (identical(other.weight, weight) || other.weight == weight) && - (identical(other.sprites, sprites) || other.sprites == sprites)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => - Object.hash(runtimeType, id, name, height, weight, sprites); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_PokemonCopyWith<_$_Pokemon> get copyWith => - __$$_PokemonCopyWithImpl<_$_Pokemon>(this, _$identity); - - @override - Map<String, dynamic> toJson() { - return _$$_PokemonToJson( - this, - ); - } -} - -abstract class _Pokemon implements Pokemon { - const factory _Pokemon( - {required final int id, - required final String name, - required final int height, - required final int weight, - required final Sprite sprites}) = _$_Pokemon; - - factory _Pokemon.fromJson(Map<String, dynamic> json) = _$_Pokemon.fromJson; - - @override - int get id; - @override - String get name; - @override - int get height; - @override - int get weight; - @override - Sprite get sprites; - @override - @JsonKey(ignore: true) - _$$_PokemonCopyWith<_$_Pokemon> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/examples/pokeapi_functional/lib/models/sprite.dart b/examples/pokeapi_functional/lib/models/sprite.dart deleted file mode 100644 index 684e159..0000000 --- a/examples/pokeapi_functional/lib/models/sprite.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'sprite.freezed.dart'; -part 'sprite.g.dart'; - -/// Pokemon sprite image, with method to deserialize json -@freezed -class Sprite with _$Sprite { - const factory Sprite({ - @JsonKey(name: 'front_default') required String frontDefault, - }) = _Sprite; - - factory Sprite.fromJson(Map<String, Object?> json) => _$SpriteFromJson(json); -} diff --git a/examples/pokeapi_functional/lib/models/sprite.freezed.dart b/examples/pokeapi_functional/lib/models/sprite.freezed.dart deleted file mode 100644 index 11103b4..0000000 --- a/examples/pokeapi_functional/lib/models/sprite.freezed.dart +++ /dev/null @@ -1,151 +0,0 @@ -// coverage:ignore-file -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: type=lint -// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark - -part of 'sprite.dart'; - -// ************************************************************************** -// FreezedGenerator -// ************************************************************************** - -T _$identity<T>(T value) => value; - -final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); - -Sprite _$SpriteFromJson(Map<String, dynamic> json) { - return _Sprite.fromJson(json); -} - -/// @nodoc -mixin _$Sprite { - @JsonKey(name: 'front_default') - String get frontDefault => throw _privateConstructorUsedError; - - Map<String, dynamic> toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $SpriteCopyWith<Sprite> get copyWith => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $SpriteCopyWith<$Res> { - factory $SpriteCopyWith(Sprite value, $Res Function(Sprite) then) = - _$SpriteCopyWithImpl<$Res, Sprite>; - @useResult - $Res call({@JsonKey(name: 'front_default') String frontDefault}); -} - -/// @nodoc -class _$SpriteCopyWithImpl<$Res, $Val extends Sprite> - implements $SpriteCopyWith<$Res> { - _$SpriteCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? frontDefault = null, - }) { - return _then(_value.copyWith( - frontDefault: null == frontDefault - ? _value.frontDefault - : frontDefault // ignore: cast_nullable_to_non_nullable - as String, - ) as $Val); - } -} - -/// @nodoc -abstract class _$$_SpriteCopyWith<$Res> implements $SpriteCopyWith<$Res> { - factory _$$_SpriteCopyWith(_$_Sprite value, $Res Function(_$_Sprite) then) = - __$$_SpriteCopyWithImpl<$Res>; - @override - @useResult - $Res call({@JsonKey(name: 'front_default') String frontDefault}); -} - -/// @nodoc -class __$$_SpriteCopyWithImpl<$Res> - extends _$SpriteCopyWithImpl<$Res, _$_Sprite> - implements _$$_SpriteCopyWith<$Res> { - __$$_SpriteCopyWithImpl(_$_Sprite _value, $Res Function(_$_Sprite) _then) - : super(_value, _then); - - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? frontDefault = null, - }) { - return _then(_$_Sprite( - frontDefault: null == frontDefault - ? _value.frontDefault - : frontDefault // ignore: cast_nullable_to_non_nullable - as String, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$_Sprite implements _Sprite { - const _$_Sprite({@JsonKey(name: 'front_default') required this.frontDefault}); - - factory _$_Sprite.fromJson(Map<String, dynamic> json) => - _$$_SpriteFromJson(json); - - @override - @JsonKey(name: 'front_default') - final String frontDefault; - - @override - String toString() { - return 'Sprite(frontDefault: $frontDefault)'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$_Sprite && - (identical(other.frontDefault, frontDefault) || - other.frontDefault == frontDefault)); - } - - @JsonKey(ignore: true) - @override - int get hashCode => Object.hash(runtimeType, frontDefault); - - @JsonKey(ignore: true) - @override - @pragma('vm:prefer-inline') - _$$_SpriteCopyWith<_$_Sprite> get copyWith => - __$$_SpriteCopyWithImpl<_$_Sprite>(this, _$identity); - - @override - Map<String, dynamic> toJson() { - return _$$_SpriteToJson( - this, - ); - } -} - -abstract class _Sprite implements Sprite { - const factory _Sprite( - {@JsonKey(name: 'front_default') - required final String frontDefault}) = _$_Sprite; - - factory _Sprite.fromJson(Map<String, dynamic> json) = _$_Sprite.fromJson; - - @override - @JsonKey(name: 'front_default') - String get frontDefault; - @override - @JsonKey(ignore: true) - _$$_SpriteCopyWith<_$_Sprite> get copyWith => - throw _privateConstructorUsedError; -} diff --git a/examples/pokeapi_functional/pubspec.yaml b/examples/pokeapi_functional/pubspec.yaml deleted file mode 100644 index 7423846..0000000 --- a/examples/pokeapi_functional/pubspec.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: pokeapi_functional -description: Functional Programming using fpdart. Fetch and display pokemon from pokeApi. -publish_to: "none" - -version: 1.0.0+1 - -environment: - sdk: ">=2.19.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - - http: ^0.13.5 - hooks_riverpod: ^2.3.6 - flutter_hooks: ^0.18.6 - freezed: ^2.3.2 - fpdart: - path: ../../packages/fpdart - json_annotation: ^4.8.0 - riverpod_annotation: ^2.1.1 - -dev_dependencies: - flutter_test: - sdk: flutter - freezed_annotation: ^2.2.0 - build_runner: ^2.4.1 - json_serializable: ^6.6.1 - riverpod_generator: ^2.2.1 - -flutter: - uses-material-design: true diff --git a/examples/pokeapi_functional/web/favicon.png b/examples/pokeapi_functional/web/favicon.png deleted file mode 100644 index 8aaa46ac1ae21512746f852a42ba87e4165dfdd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l<c?^6clWVQqrt~T->1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*<mM6;!yDy;iI!?0xZxS-@9o){@>SYAX-%d(5<QWisP% zpKxJ^!zO{CB{zx^b({)M+}nHh%-rj7^M&&ZcU$j%zjx;LVyDXoIuhq|8P;5s=KHG5 z<JUB4<A0$RmNJizixz(PY{v3_`K1HjE{oV$3+pLbhDf-dGF^GSkNIsaZ^^?aj@RFX zmI#!cuT=hV*<Wg+koNS~T)(gK8EvWbJGDN%Cgswi@;rmS$PYP}{^d1_2Ol_C#PhDD zFw$(<^hU$>gVjrHJWqXQshj@!<B9Fv+a9JEO;0$m^uEygb*U`#RIBf=|21JrRHoLF zSVqsI&*oWt$jm+Fz?pMwCX>Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM diff --git a/examples/pokeapi_functional/web/icons/Icon-192.png b/examples/pokeapi_functional/web/icons/Icon-192.png deleted file mode 100644 index b749bfef07473333cf1dd31e9eed89862a5d52aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5292 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 diff --git a/examples/pokeapi_functional/web/icons/Icon-512.png b/examples/pokeapi_functional/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48dff1169879ba46840804b412fe02fefd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8252 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 diff --git a/examples/pokeapi_functional/web/index.html b/examples/pokeapi_functional/web/index.html deleted file mode 100644 index 1734d47..0000000 --- a/examples/pokeapi_functional/web/index.html +++ /dev/null @@ -1,98 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <!-- - If you are serving your web app in a path other than the root, change the - href value below to reflect the base path you are serving from. - - The path provided below has to start and end with a slash "/" in order for - it to work correctly. - - For more details: - * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base - --> - <base href="/"> - - <meta charset="UTF-8"> - <meta content="IE=Edge" http-equiv="X-UA-Compatible"> - <meta name="description" content="A new Flutter project."> - - <!-- iOS meta tags & icons --> - <meta name="apple-mobile-web-app-capable" content="yes"> - <meta name="apple-mobile-web-app-status-bar-style" content="black"> - <meta name="apple-mobile-web-app-title" content="pokeapi_functional"> - <link rel="apple-touch-icon" href="icons/Icon-192.png"> - - <title>pokeapi_functional</title> - <link rel="manifest" href="manifest.json"> -</head> -<body> - <!-- This script installs service_worker.js to provide PWA functionality to - application. For more information, see: - https://developers.google.com/web/fundamentals/primers/service-workers --> - <script> - var serviceWorkerVersion = null; - var scriptLoaded = false; - function loadMainDartJs() { - if (scriptLoaded) { - return; - } - scriptLoaded = true; - var scriptTag = document.createElement('script'); - scriptTag.src = 'main.dart.js'; - scriptTag.type = 'application/javascript'; - document.body.append(scriptTag); - } - - if ('serviceWorker' in navigator) { - // Service workers are supported. Use them. - window.addEventListener('load', function () { - // Wait for registration to finish before dropping the <script> tag. - // Otherwise, the browser will load the script multiple times, - // potentially different versions. - var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion; - navigator.serviceWorker.register(serviceWorkerUrl) - .then((reg) => { - function waitForActivation(serviceWorker) { - serviceWorker.addEventListener('statechange', () => { - if (serviceWorker.state == 'activated') { - console.log('Installed new service worker.'); - loadMainDartJs(); - } - }); - } - if (!reg.active && (reg.installing || reg.waiting)) { - // No active web worker and we have installed or are installing - // one for the first time. Simply wait for it to activate. - waitForActivation(reg.installing ?? reg.waiting); - } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) { - // When the app updates the serviceWorkerVersion changes, so we - // need to ask the service worker to update. - console.log('New service worker available.'); - reg.update(); - waitForActivation(reg.installing); - } else { - // Existing service worker is still good. - console.log('Loading app from service worker.'); - loadMainDartJs(); - } - }); - - // If service worker doesn't succeed in a reasonable amount of time, - // fallback to plaint <script> tag. - setTimeout(() => { - if (!scriptLoaded) { - console.warn( - 'Failed to load app from service worker. Falling back to plain <script> tag.', - ); - loadMainDartJs(); - } - }, 4000); - }); - } else { - // Service workers not supported. Just drop the <script> tag. - loadMainDartJs(); - } - </script> -</body> -</html> diff --git a/examples/pokeapi_functional/web/manifest.json b/examples/pokeapi_functional/web/manifest.json deleted file mode 100644 index 9877306..0000000 --- a/examples/pokeapi_functional/web/manifest.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "pokeapi_functional", - "short_name": "pokeapi_functional", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - } - ] -} diff --git a/examples/read_write_file/.gitignore b/examples/read_write_file/.gitignore deleted file mode 100644 index 570e1df..0000000 --- a/examples/read_write_file/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.dart_tool/ -.packages -pubspec.lock - -# Generated files -**.g.dart -.idea/ \ No newline at end of file diff --git a/examples/read_write_file/assets/source_eng.txt b/examples/read_write_file/assets/source_eng.txt deleted file mode 100644 index 05fecd2..0000000 --- a/examples/read_write_file/assets/source_eng.txt +++ /dev/null @@ -1,8 +0,0 @@ -This page collects information about Giulianova Calcio in the official competitions of the 1982-1983 season. -A fossil group of galaxies consists of a gigantic elliptical galaxy resulting from the fusion of the galaxies that originally formed the group. -Born and designed for distribution in cinemas, it enhances the artistic and dramaturgical aspect, often with particular virtuosity in both photography and especially in the screenplay, editing and critical and authoritative analysis of content. -This position is represented and uniquely identified by a series of alphanumeric symbols, a code called ‘location marking’ and identified in precise ways. -The capital of the state itself bears the name Bihar. -In the 97-98 season the first Czech division changed its name from 1. -On 21 December 1944, a "National Council" was formed in Debrecen with the approval of the Soviet Union and the participation of some members of the Hungarian Communist Party, such as Ernő Gerő, László Rajk and later Mátyás Rákosi. -It was given as a wife in 1447 to Duke John II of Bourbon. \ No newline at end of file diff --git a/examples/read_write_file/assets/source_ita.txt b/examples/read_write_file/assets/source_ita.txt deleted file mode 100644 index d8c53cb..0000000 --- a/examples/read_write_file/assets/source_ita.txt +++ /dev/null @@ -1,8 +0,0 @@ -Questa pagina raccoglie le informazioni riguardanti il Giulianova Calcio nelle competizioni ufficiali della stagione 1982-1983. -Un gruppo fossile di galassie è costituito da una gigantesca galassia ellittica risultato della fusione delle galassie che originariamente formavano il gruppo. -Nato e pensato per la distribuzione nei cinema, esalta l'aspetto artistico e drammaturgico, spesso con particolare virtuosismo sia nella fotografia che soprattutto nella sceneggiatura, nel montaggio e nell'analisi critica e autoriale dei contenuti. -Tale posizione è rappresentata e individuata, in modo univoco, da una serie di simboli alfanumerici, un codice chiamato «segnatura di collocazione» e individuato secondo precise modalità. -La stessa capitale dello stato porta il nome di Bihar. -Nella stagione 97-98 la prima divisione ceca cambia nome da 1. -Il 21 dicembre 1944, un "Consiglio nazionale" si costituì a Debrecen con l'approvazione dell'Unione Sovietica e la partecipazione di alcuni membri del Partito Comunista Ungherese, come Ernő Gerő, László Rajk e più tardi Mátyás Rákosi. -Venne data in moglie nel 1447 al duca Giovanni II di Borbone. \ No newline at end of file diff --git a/examples/read_write_file/lib/file_system.dart b/examples/read_write_file/lib/file_system.dart deleted file mode 100644 index f13db9e..0000000 --- a/examples/read_write_file/lib/file_system.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:fpdart/fpdart.dart'; - -sealed class FileSystemError { - const FileSystemError(); -} - -class ReadFileError extends FileSystemError { - const ReadFileError(); -} - -abstract class FileSystem { - Effect<File, FileSystemError, List<String>> readAsLines({ - Encoding encoding = utf8, - }); -} - -final class FileSystemLive extends FileSystem { - Effect<File, FileSystemError, List<String>> readAsLines({ - Encoding encoding = utf8, - }) => - Effect.env<File, FileSystemError>().flatMap( - (file) => Effect.tryCatch( - () async => file.readAsLines(encoding: encoding), - (error, stackTrace) => const ReadFileError(), - ), - ); -} diff --git a/examples/read_write_file/lib/main.dart b/examples/read_write_file/lib/main.dart deleted file mode 100644 index 0dce251..0000000 --- a/examples/read_write_file/lib/main.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'dart:io'; - -import 'package:fpdart/fpdart.dart'; - -import 'file_system.dart'; -import 'program.dart'; - -/** - * Read lines from a `.txt` file using [Effect] of fpdart. - * - * This application reads from two files containing english and italian sentences. - * It then uses `zip` to join the two resulting lists together in a `List<(String, String)>`. - */ - -void main() async { - final main = program - .flatMap( - (list) => Effect.function( - () { - list.forEach( - (e) => print( - '${e.index}, ${e.word}(${e.wordIndex}): ${e.english}_${e.italian}\n'), - ); - }, - ), - ) - .catchError( - (error) => Effect.function( - () { - print(error); - }, - ), - ); - - await main.runFuture( - ( - searchWords: const ['that', 'and', 'for'], - fileSystem: FileSystemLive(), - sourceEng: File('./assets/source_eng.txt'), - sourceIta: File('./assets/source_ita.txt'), - ), - ); -} diff --git a/examples/read_write_file/lib/program.dart b/examples/read_write_file/lib/program.dart deleted file mode 100644 index b0d1589..0000000 --- a/examples/read_write_file/lib/program.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'dart:io'; - -import 'package:fpdart/fpdart.dart'; - -import 'file_system.dart'; - -class _FoundWord { - final int index; - final String word; - final int wordIndex; - final String english; - final String italian; - const _FoundWord( - this.index, - this.word, - this.wordIndex, - this.english, - this.italian, - ); -} - -Iterable<_FoundWord> _collectFoundWords( - List<String> searchWords, - Iterable<(String, String)> iterable, -) => - iterable.flatMapWithIndex( - (tuple, index) => searchWords.foldLeftWithIndex<List<_FoundWord>>( - [], - (acc, word, wordIndex) => - tuple.$2.toLowerCase().split(' ').contains(word) - ? [ - ...acc, - _FoundWord( - index, - word, - wordIndex, - tuple.$2.replaceAll(word, '<\$>'), - tuple.$1, - ), - ] - : acc, - ), - ); - -typedef Env = ({ - FileSystem fileSystem, - File sourceIta, - File sourceEng, - List<String> searchWords, -}); - -final program = Effect<Env, FileSystemError, Iterable<_FoundWord>>.gen( - (_) async { - final env = await _(Effect.env()); - - final linesIta = await _( - env.fileSystem.readAsLines().provide( - (env) => env.sourceIta, - ), - ); - - final linesEng = await _( - env.fileSystem.readAsLines().provide( - (env) => env.sourceEng, - ), - ); - - final linesZip = linesIta.zip(linesEng); - return _collectFoundWords(env.searchWords, linesZip); - }, -); diff --git a/examples/read_write_file/pubspec.yaml b/examples/read_write_file/pubspec.yaml deleted file mode 100644 index 084e925..0000000 --- a/examples/read_write_file/pubspec.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: fpdart_read_write_file -publish_to: none -version: 0.1.0 -homepage: https://www.sandromaglione.com/ -repository: https://github.com/SandroMaglione/fpdart -description: Example of Functional programming in Dart and Flutter using fpdart. Read and write local text file. -author: Maglione Sandro <lass.maglio@gmail.com> - -environment: - sdk: ">=3.0.0 <4.0.0" - -dependencies: - fpdart: - path: ../../packages/fpdart - -dev_dependencies: - lint: ^1.5.3 - test: ^1.25.2 diff --git a/examples/read_write_file/test/main_test.dart b/examples/read_write_file/test/main_test.dart deleted file mode 100644 index 8790150..0000000 --- a/examples/read_write_file/test/main_test.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:fpdart/src/effect.dart'; -import 'package:fpdart_read_write_file/file_system.dart'; -import 'package:fpdart_read_write_file/program.dart'; -import 'package:test/test.dart'; - -final class FileSystemTest extends FileSystem { - @override - Effect<File, FileSystemError, List<String>> readAsLines( - {Encoding encoding = utf8}) => - Effect.succeed( - ["a sentence here"], - ); -} - -void main() { - test('program', () async { - final result = await program.runFuture( - ( - searchWords: const ['sentence'], - fileSystem: FileSystemTest(), - sourceEng: File(''), - sourceIta: File(''), - ), - ); - - expect( - result.map((e) => e.english).toList(), - ['a <\$> here'], - ); - }); -} diff --git a/packages/fpdart/example/api_requests/try_catch_validation.dart b/packages/fpdart/example/api_requests/try_catch_validation.dart deleted file mode 100644 index 5cf6593..0000000 --- a/packages/fpdart/example/api_requests/try_catch_validation.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'dart:convert'; - -import 'package:fpdart/fpdart.dart'; - -/// Example of API request with `fpdart` with validation -/// Source: https://github.com/SandroMaglione/fpdart/issues/50#issue-1372504529 - -/// Mock [Response] implementation -class Response { - final String body; - Response(this.body); -} - -/// Mock for `post` API request -Response post( - Uri uri, { - Map<String, String>? headers, -}) => - Response(''); - -TaskEither<String, String> request() => TaskEither.tryCatch( - () async { - final Response getPrice = await post( - Uri.parse("URL"), - headers: { - 'Content-Type': 'application/json; charset=UTF-8', - }, - ); - - final Map<String, dynamic> json = - jsonDecode(getPrice.body) as Map<String, dynamic>; - - if (!json.containsKey("pricing")) { - throw Exception("I don't have price"); - } - - return json["pricing"].toString(); - }, - (error, stackTrace) { - return error.toString(); - }, - ); - -/// Instead of placing all the request + validation inside `tryCatch` -/// we want to chain different [TaskEither] methods. -/// -/// This allows to create a pipeline where each step is responsible -/// for a specific purpose (request, extract parameters, validation). -/// -/// It's also important to implement a solid error reporting system, -/// ideally by adding our own [Error] class. -/// -/// Finally, in order for the request to be a **pure function** we want to -/// pass all the parameters as inputs to the function -typedef Pricing = String; - -abstract class RequestError { - String get message; -} - -class ApiRequestError implements RequestError { - final Object error; - final StackTrace stackTrace; - - ApiRequestError(this.error, this.stackTrace); - - @override - String get message => "Error in the API request"; -} - -class MissingPricingRequestError implements RequestError { - @override - String get message => "Missing pricing in API response"; -} - -TaskEither<RequestError, Response> makeRequest(String url) => - TaskEither<RequestError, Response>.tryCatch( - () async => post( - Uri.parse(url), - headers: { - 'Content-Type': 'application/json; charset=UTF-8', - }, - ), - (error, stackTrace) => ApiRequestError(error, stackTrace), - ); - -Map<String, dynamic> mapToJson(Response response) => - jsonDecode(response.body) as Map<String, dynamic>; - -TaskEither<RequestError, Map<String, dynamic>> mappingRequest(String url) => - makeRequest(url).map(mapToJson); - -TaskEither<RequestError, String> validationRequest(Map<String, dynamic> json) => - !json.containsKey("pricing") - ? TaskEither.left(MissingPricingRequestError()) - : TaskEither.of(json["pricing"].toString()); - -TaskEither<RequestError, Pricing> requestTE(String url) => - makeRequest(url).map(mapToJson).flatMap(validationRequest); - -/// **Note**: Ideally we should not access `post`, `Uri.parse`, and `jsonDecode` inside the function. -/// -/// We should instead pass them as inputs to the function. This will allow to make the function -/// completely pure, without hidden dependencies (i.e. accessing variables in the global scope). -/// -/// Furthermore, doing this will help with testing, since we can provide our own mock -/// implementation of those function for testing purposes. diff --git a/packages/fpdart/example/do_notation/main.dart b/packages/fpdart/example/do_notation/main.dart deleted file mode 100644 index adec717..0000000 --- a/packages/fpdart/example/do_notation/main.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -TaskEither<String, String> getUsernameFromId(int id) => TaskEither.of('sandro'); -TaskEither<String, String> getProfilePicture(String username) => - TaskEither.of('image'); -int getPictureWidth(String image) => 10; -TaskEither<String, bool> updatePictureWidth(int width) => TaskEither.of(true); - -Future<String> getUsernameFromIdLinear(int id) async => 'sandro'; -Future<String> getProfilePictureLinear(String username) async => 'image'; -int getPictureWidthLinear(String image) => 10; -Future<bool> updatePictureWidthLinear(int width) async => true; - -/// Linear (no fpdart) -Future<bool> changePictureSizeFromIdLinear(int id) async { - final username = await getUsernameFromIdLinear(id); - final image = await getProfilePictureLinear(username); - final width = getPictureWidthLinear(image); - return updatePictureWidthLinear(width); -} - -/// Chaining -TaskEither<String, bool> changePictureSizeFromId(int id) => - getUsernameFromId(id) - .flatMap((username) => getProfilePicture(username)) - .map((image) => getPictureWidth(image)) - .flatMap((width) => updatePictureWidth(width)); - -/// Do notation -TaskEither<String, bool> changePictureSizeFromIdDo(int id) => - TaskEither<String, bool>.Do( - (_) async { - final username = await _(getUsernameFromId(id)); - final image = await _(getProfilePicture(username)); - final width = getPictureWidth(image); - return _(updatePictureWidth(width)); - }, - ); - -/// [map]: Update value inside [Option] -Option<int> map() => Option.of(10) - .map( - (a) => a + 1, - ) - .map( - (b) => b * 3, - ) - .map( - (c) => c - 4, - ); - -Option<int> mapDo() => Option.Do((_) { - final a = _(Option.of(10)); - final b = a + 1; - final c = b * 3; - return c - 4; - }); - -/// [flatMap]: Chain [Option] -Option<int> flatMap() => Option.of(10) - .flatMap( - (a) => Option.of(a + 1), - ) - .flatMap( - (b) => Option.of(b * 3), - ) - .flatMap( - (c) => Option.of(c - 4), - ); - -Option<int> flatMapDo() => Option.Do((_) { - final a = _(Option.of(10)); - final b = _(Option.of(a + 1)); - final c = _(Option.of(b * 3)); - return _(Option.of(c - 4)); - }); - -/// [andThen]: Chain [Option] without storing its value -Option<int> andThen() => Option.of(10).andThen(() => Option.of(20)); -Option<int> andThenDo() => Option.Do((_) { - _(Option.of(10)); // Chain Option, but do not store the result - return 20; - }); diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index 8ce79f4..0f8232d 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -1,28 +1,28 @@ import 'package:fpdart/fpdart.dart'; void main() async { - final effect = Effect.tryCatch( + final effect = Effect<Never, String, int>.tryCatch( () => Future.value(10), (error, stackTrace) => "10", ); - final effect1 = Effect.function(() => 10); + final effect1 = Effect<Never, String, int>.function(() => 10); - final doing = Effect<int, String, int>.gen( + final main = Effect<int, String, int>.gen( (_) async { final env = await _(Effect.env()); - final beforeEnv = await _(effect.provide(identity)); - final e1 = await _(effect1.mapError((l) => "null").provide(identity)); + final beforeEnv = await _(effect.withEnv()); + final e1 = await _(effect1.mapError((l) => "null").withEnv()); - final mapped = await _(effect.map((r) => r + 10).provide(identity)); + final mapped = await _(effect.map((r) => r + 10).withEnv()); final asEither = await _(Right<String, int>(10).provide<int>()); final asOption = await _(Some<int>(10).provide(() => "Some")); return beforeEnv + mapped + asEither + asOption + e1; }, ); - print(doing); + print(main); - final run = await doing(10); + final run = await main(10); print(run); } diff --git a/packages/fpdart/example/logger/logger.dart b/packages/fpdart/example/logger/logger.dart deleted file mode 100644 index 9b195ab..0000000 --- a/packages/fpdart/example/logger/logger.dart +++ /dev/null @@ -1,78 +0,0 @@ -enum Level { - verbose, - debug, - info, - warning, - error, - wtf, - nothing, -} - -class LogEvent { - final Level level; - final dynamic message; - final dynamic error; - final StackTrace? stackTrace; - - LogEvent(this.level, this.message, this.error, this.stackTrace); -} - -class OutputEvent { - final Level level; - final List<String> lines; - - OutputEvent(this.level, this.lines); -} - -abstract class LogFilter { - bool shouldLog(LogEvent logEvent); -} - -abstract class LogPrinter { - List<String> log(LogEvent logEvent); -} - -abstract class LogOutput { - void output(OutputEvent outputEvent); -} - -class Logger { - static Level level = Level.verbose; - final bool _active = true; - final LogFilter _filter; - final LogPrinter _printer; - final LogOutput _output; - - Logger(this._filter, this._printer, this._output); - - /// Log a message with [level]. - void log( - Level level, - dynamic message, [ - dynamic error, - StackTrace? stackTrace, - ]) { - if (!_active) { - throw ArgumentError('Logger has already been closed.'); - } else if (error != null && error is StackTrace) { - throw ArgumentError('Error parameter cannot take a StackTrace!'); - } else if (level == Level.nothing) { - throw ArgumentError('Log events cannot have Level.nothing'); - } - var logEvent = LogEvent(level, message, error, stackTrace); - - if (_filter.shouldLog(logEvent)) { - var output = _printer.log(logEvent); - - if (output.isNotEmpty) { - var outputEvent = OutputEvent(level, output); - try { - _output.output(outputEvent); - } catch (e, s) { - print(e); - print(s); - } - } - } - } -} diff --git a/packages/fpdart/example/logger/main.dart b/packages/fpdart/example/logger/main.dart deleted file mode 100644 index a6d1fec..0000000 --- a/packages/fpdart/example/logger/main.dart +++ /dev/null @@ -1,121 +0,0 @@ -/// Convert `log` function from `logger` package -/// from Imperative to Functional code using `fpdart` -/// -/// Repository: https://github.com/leisim/logger -import 'package:fpdart/fpdart.dart'; - -import 'logger.dart'; - -class Logger { - static Level level = Level.verbose; - bool _active = true; - final LogFilter _filter; - final LogPrinter _printer; - final LogOutput _output; - Logger(this._filter, this._printer, this._output); - - /// Imperative (not-functional) code - /// - /// From https://github.com/leisim/logger/blob/6832ee0f5c430321f6a74dce99338b242861161d/lib/src/logger.dart#L104 - void log( - Level level, - dynamic message, [ - dynamic error, - StackTrace? stackTrace, - ]) { - if (!_active) { - throw ArgumentError('Logger has already been closed.'); - } else if (error != null && error is StackTrace) { - throw ArgumentError('Error parameter cannot take a StackTrace!'); - } else if (level == Level.nothing) { - throw ArgumentError('Log events cannot have Level.nothing'); - } - var logEvent = LogEvent(level, message, error, stackTrace); - - if (_filter.shouldLog(logEvent)) { - var output = _printer.log(logEvent); - - if (output.isNotEmpty) { - var outputEvent = OutputEvent(level, output); - try { - _output.output(outputEvent); - } catch (e, s) { - print(e); - print(s); - } - } - } - } -} - -/// Functional approach 💪 -/// ---------------------------------------------------------------- -/// Use [IOEither] to handle errors and avoid throwing exceptions 🔨 -/// -/// Use [Unit] instead of `void` to represent a function that returns nothing 🎭 -IOEither<String, Unit> logFunctional({ - required Level level, - required dynamic message, - required dynamic error, - StackTrace? stackTrace, - - /// Add all external dependencies as input to make the function pure 🥼 - required bool active, - required LogFilter filter, - required LogPrinter printer, - required LogOutput output, -}) { - /// Handle errors using [Either] instead of throwing errors 💥 - if (!active) { - return IOEither.left('Logger has already been closed.'); - } else if (error != null && error is StackTrace) { - return IOEither.left('Error parameter cannot take a StackTrace!'); - } else if (level == Level.nothing) { - return IOEither.left('Log events cannot have Level.nothing'); - } - - /// Declare all the variables as `const` or `final` 🧱 - final logEvent = LogEvent(level, message, error, stackTrace); - - /// Make sure to handle all the cases using [Option] 🎉 - /// - /// Use the `identity` function to return the input parameter as it is - final shouldLogOption = Option.fromPredicate( - filter.shouldLog(logEvent), - identity, - ); - - /// Using [Option], you must specify both `true` and `false` cases 🌎 - return shouldLogOption.match( - /// Simply return a [Unit] in the else case 🎁 - () => IOEither.of(unit), - - /// Use another [Option] to evaluate `printer.log` - (_) => Option<List<String>>.fromPredicate( - printer.log(logEvent), - (v) => v.isNotEmpty, - ).match( - /// Simply return a [Unit] in the else case 🎁 - () => IOEither.of(unit), - - (lines) { - /// All variables are `final` 🧱 - final outputEvent = OutputEvent(level, lines); - return IOEither<String, Unit>.tryCatch( - () { - output.output(outputEvent); - - /// Return [Unit] 🎁 - return unit; - }, - (e, s) { - /// Return an error message 🔨 - /// - /// Do not `print`, it would make the function impure! 🤯 - return 'An error occurred: $e'; - }, - ); - }, - ), - ); -} diff --git a/packages/fpdart/example/main.dart b/packages/fpdart/example/main.dart index b046fff..ab73b3a 100644 --- a/packages/fpdart/example/main.dart +++ b/packages/fpdart/example/main.dart @@ -1,124 +1 @@ -import 'package:fpdart/fpdart.dart'; - void main() {} - -void overview() { - /// [Option] - const int? a = null; - final Option<int> b = none<int>(); - - /// You must manually handle missing values - int resultI = 0; - if (a != null) { - resultI = a * 2; - } - - /// No need to check for `null` - final resultF = b.getOrElse(() => 0) * 2; -} - -void imperativeVSfunctional() { - /// Sum elements of a list - const List<int> list = [1, 2, 3, 4]; - - /// Imperative solution - int sumI = 0; - for (int i = 0; i < list.length; ++i) { - sumI = sumI + list[i]; - } - - /// Functional solution - final sumF = list.fold<int>(0, (p, c) => p + c); - - /// Composability - /// Sum all elements of a list that are greater than 2 - /// Imperative solution - int sum2I = 0; - for (int i = 0; i < list.length; ++i) { - final value = list[i]; - if (value > 2) { - sum2I = sum2I + value; - } - } - - /// Functional solution - final sum2F = list.where((e) => e > 2).fold<int>(0, (p, c) => p + c); - - /// Extreme example - /// - /// How can you achieve the same result with Imperative code? - /// Is it even possible? 🤷♂️ - final result = list - .where((e) => e > 2) - .concat([1, 2, 3]) - .drop(2) - .intersect([1, 2, 3]) - .map((e) => e * 2) - .take(3) - .first; -} - -void option() { - /// Create an instance of [Some] - final option = Option.of(10); - - /// Create an instance of [None] - final none = Option<int>.none(); - - /// Map [int] to [String] - final map = option.map((a) => '$a'); - - /// Extract the value from [Option] - final value = option.getOrElse(() => -1); - - /// Pattern matching - final match = option.match( - () => print('None'), - (a) => print('Some($a)'), - ); - - /// Convert to [Either] - final either = option.toEither(() => 'missing'); - - /// Chain computations - final flatMap = option.flatMap((a) => Option.of(a + 10)); - - /// Return [None] if the function throws an error - final tryCatch = Option.tryCatch(() => int.parse('invalid')); -} - -void either() { - /// Create an instance of [Right] - final right = Either<String, int>.of(10); - - /// Create an instance of [Left] - final left = Either<String, int>.left('none'); - - /// Map the right value to a [String] - final mapRight = right.map((a) => '$a'); - - /// Map the left value to a [int] - final mapLeft = right.mapLeft((a) => a.length); - - /// Return [Left] if the function throws an error. - /// Otherwise return [Right]. - final tryCatch = Either.tryCatch( - () => int.parse('invalid'), - (e, s) => 'Error: $e', - ); - - /// Extract the value from [Either] - final value = right.getOrElse((l) => -1); - - /// Chain computations - final flatMap = right.flatMap((a) => Either.of(a + 10)); - - /// Pattern matching - final match = right.match( - (l) => print('Left($l)'), - (r) => print('Right($r)'), - ); - - /// Convert to [Option] - final option = right.toOption(); -} diff --git a/packages/fpdart/example/src/either/cast.dart b/packages/fpdart/example/src/either/cast.dart deleted file mode 100644 index f6dc0ac..0000000 --- a/packages/fpdart/example/src/either/cast.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -void main() { - int intValue = 10; - - /// Unhandled exception: type 'int' is not a subtype of type 'List<int>' in type cast - final waitWhat = intValue as List<int>; - final first = waitWhat.first; - print(first); - - /// Safe 🎯 - final wellYeah = Either<String, List<int>>.safeCast( - intValue, - (dynamic value) => 'Not an List!', - ); - final firstEither = wellYeah.map((list) => list.first); - print(firstEither); - - /// Verify using `is` - dynamic locationJson = 0; - - if (locationJson is List<int>) { - final first = locationJson.first; - print(first); - } -} diff --git a/packages/fpdart/example/src/either/chain_either.dart b/packages/fpdart/example/src/either/chain_either.dart deleted file mode 100644 index 0dca649..0000000 --- a/packages/fpdart/example/src/either/chain_either.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -class Cart { - const Cart(); -} - -class User { - String get uid => ''; -} - -class Order { - const Order(); - factory Order.fromCart({required String userId, required Cart cart}) { - return Order(); - } -} - -class AuthRepository { - User? get currentUser { - return null; - } -} - -class CartRepository { - Cart fetchCart(String uid) => Cart(); - Future<void> setCart(String uid, Cart cart) { - return Future.value(); - } -} - -class OrdersRepository { - Future<void> addOrder(String uid, Order order) { - return Future.value(); - } -} - -final cartRepository = CartRepository(); -final authRepository = AuthRepository(); -final ordersRepository = OrdersRepository(); - -Future<void> placeOrder() async { - /// Imperative try-catch code - /// Source: https://codewithandrea.com/articles/flutter-exception-handling-try-catch-result-type/#when-the-result-type-doesnt-work-well - try { - final uid = authRepository.currentUser!.uid; - // first await call - final cart = await cartRepository.fetchCart(uid); - final order = Order.fromCart(userId: uid, cart: cart); - // second await call - await ordersRepository.addOrder(uid, order); - // third await call - await cartRepository.setCart(uid, const Cart()); - } catch (e) { - // TODO: Handle exceptions from any of the methods above - } - - /// Same code using fpart and Functional programming - Either.fromNullable( - authRepository.currentUser?.uid, - () => 'Missing uid', - ).toTaskEither().flatMap( - (uid) => TaskEither.tryCatch( - () async => cartRepository.fetchCart(uid), - (_, __) => 'Error while fetching cart', - ) - .flatMap( - (cart) => TaskEither.tryCatch( - () async => ordersRepository.addOrder( - uid, - Order.fromCart( - userId: uid, - cart: cart, - ), - ), - (_, __) => 'Error while adding order', - ), - ) - .flatMap( - (_) => TaskEither.tryCatch( - () async => cartRepository.setCart( - uid, - const Cart(), - ), - (_, __) => 'Error while setting cart', - ), - ), - ); -} diff --git a/packages/fpdart/example/src/either/either1.dart b/packages/fpdart/example/src/either/either1.dart deleted file mode 100644 index 50d7645..0000000 --- a/packages/fpdart/example/src/either/either1.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -int? getNoEither(int index, List<int> list) { - if (index < 0 || index >= list.length) { - return null; - } - - return list[index]; -} - -Either<String, int> getEither(int index, List<int> list) { - if (index < 0 || index >= list.length) { - return Either.left('index not valid'); - } - - return Either.of(list[index]); -} - -int multiply(int value) => value * 2; - -void main() { - const list = [1, 2, 3]; - - /// Without [Either], you must check that the value is not null. - /// You must also remember to handle the case in which the value is null, - /// what would happen then? - final noEither = getNoEither(-1, list); - if (noEither != null) { - print(multiply(noEither)); - } - - /// With [Either], you are required to handle all cases. You will never run - /// in unspecified edge cases. - final withEither = getEither(-1, list); - withEither.match((l) => print(l), (r) => print(multiply(r))); -} diff --git a/packages/fpdart/example/src/either/overview.dart b/packages/fpdart/example/src/either/overview.dart deleted file mode 100644 index f22dd7a..0000000 --- a/packages/fpdart/example/src/either/overview.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -/// Don't do that! ⚠ -int divideI(int x, int y) => x ~/ y; // this will throw if y == 0 - -/// Error handling without exceptions using [Either] 🎉 -Either<String, int> divideF(int x, int y) { - if (y == 0) { - return left('Cannot divide by 0'); - } - return right(x ~/ y); -} - -/// Error handling with exceptions using [Either] 🎉 -Either<String, int> divide2F(int x, int y) { - /// Easy way with caveat: first param type 'object' due to dart limitation - return Either.tryCatch(() => x ~/ y, (o, s) => o.toString()); -} - -void main() { - /// Create an instance of [Right] - final right = Either<String, int>.of(10); - - /// Create an instance of [Left] - final left = Either<String, int>.left('none'); - - /// Map the right value to a [String] - final mapRight = right.map((a) => '$a'); - - /// Map the left value to a [int] - final mapLeft = right.mapLeft((a) => a.length); - - /// Return [Left] if the function throws an error. - /// Otherwise return [Right]. - final tryCatch = Either.tryCatch( - () => int.parse('invalid'), - (e, s) => 'Error: $e', - ); - - /// Extract the value from [Either] - final value = right.getOrElse((l) => -1); - - /// Chain computations - final flatMap = right.flatMap((a) => Either.of(a + 10)); - - /// Pattern matching - final match = right.match( - (l) => print('Left($l)'), - (r) => print('Right($r)'), - ); - - /// or use Dart's pattern matching as well 🤝 - final dartMatch = switch (right) { - Left(value: final l) => 'Left($l)', - Right(value: final r) => 'Right($r)', - }; - - /// Convert to [Option] - final option = right.toOption(); -} diff --git a/packages/fpdart/example/src/either/shopping/functional.dart b/packages/fpdart/example/src/either/shopping/functional.dart deleted file mode 100644 index 75a519e..0000000 --- a/packages/fpdart/example/src/either/shopping/functional.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -class Market { - const Market(); - - // I want to buy a Banana, an Apple, and a Pear. If either one - // of these is missing, I will not but anything 😒 - Either<String, String> buyBanana() => getRandomEither('🍌', "We got no 🍌"); - Either<String, String> buyApple() => getRandomEither('🍎', "We got no 🍎"); - Either<String, String> buyPear() => getRandomEither('🍐', "We got no 🍐"); - - Either<String, int> buyAmount() => - getRandomEither(randomInt(1, 10).run(), "Empty 💁🏼♂️"); -} - -Either<L, R> getRandomEither<L, R>(R right, L left) => randomBool - .map<Either<L, R>>( - (isValid) => isValid ? Either.of(right) : Either.left(left), - ) - .run(); - -// I go shopping in the Shopping Center. If it is closed, then -// I will go to the Local Market (which is always open 🥇). -Either<String, Market> goToShoppingCenter() => - getRandomEither(const Market(), "Shopping center closed ☝️"); -Either<String, Market> goToLocalMarket() => Either.of(const Market()); - -// Combine all the instructions and go shopping! 🛒 -String goShopping() => goToShoppingCenter() - .alt(goToLocalMarket) - .flatMap( - (market) => market.buyBanana().flatMap( - (banana) => market.buyApple().flatMap( - (apple) => market.buyPear().flatMap( - (pear) => Either.of('Shopping: $banana, $apple, $pear'), - ), - ), - ), - ) - .getOrElse(identity); - -// Combine all the instructions and go shopping! 🛒 -String goShoppingDo() => Either<String, String>.Do( - (_) { - final market = _(goToShoppingCenter().alt(goToLocalMarket)); - final amount = _(market.buyAmount()); - - final banana = _(market.buyBanana()); - final apple = _(market.buyApple()); - final pear = _(market.buyPear()); - - return 'Shopping: $banana, $apple, $pear'; - }, - ).getOrElse(identity); - -// Combine all the instructions and go shopping! 🛒 -String goShoppingDoFlatMap() => goToShoppingCenter() - .alt(goToLocalMarket) - .flatMap( - /// Not required types here, since [Left] inferred from chain, - /// and [Right] from the return type of `Do` - (market) => Either.Do((_) { - final banana = _(market.buyBanana()); - final apple = _(market.buyApple()); - final pear = _(market.buyPear()); - return 'Shopping: $banana, $apple, $pear'; - }), - ) - .getOrElse(identity); - -void main() { - for (int i = 0; i < 100; i++) { - final shopping = goShopping(); - print(shopping); - } - - for (int i = 0; i < 100; i++) { - final shopping = goShoppingDo(); - print('[Do]: $shopping'); - } -} diff --git a/packages/fpdart/example/src/function/const_f.dart b/packages/fpdart/example/src/function/const_f.dart deleted file mode 100644 index 3bdc381..0000000 --- a/packages/fpdart/example/src/function/const_f.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -void main() { - final c = constF<int>(10); - print(c('none')); // -> 10 - print(c('any')); // -> 10 - print(c(112.12)); // -> 10 -} diff --git a/packages/fpdart/example/src/function/curry.dart b/packages/fpdart/example/src/function/curry.dart deleted file mode 100644 index c0dd765..0000000 --- a/packages/fpdart/example/src/function/curry.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -int sum(int param1, int param2) => param1 + param2; - -double sumMultiplyDivide(int param1, int param2, int param3, int param4) => - (param1 + param2) * param3 / param4; - -void main() { - /// Convert a function with 2 parameters to a function that - /// takes the first parameter and returns a function that takes - /// the seconds parameter. - final sumCurry = sum.curry; - final sumBy2 = sumCurry(2); - final sumBy10 = sumCurry(10); - print(sumBy2(10)); - print(sumBy10(2)); - - /// Same as above but with 4 parameters. - final sumMultiplyDivideCurry = sumMultiplyDivide.curryAll; - final sumBy5 = sumMultiplyDivideCurry(5); - final multiplyBy2 = sumBy5(2); - final divideBy3 = multiplyBy2(3); - print(divideBy3(10)); - print(sumMultiplyDivideCurry(5)(2)(3)(10)); - - /// Using the extension - final sumBy2Extension = sum.curry(2); - final sumBy10Extension = sum.curry(10); - print(sumBy2Extension(10)); - print(sumBy10Extension(2)); - - final fourParamsCurry = sumMultiplyDivide.curryAll; - final fourParamsUncurry = fourParamsCurry.uncurry; -} diff --git a/packages/fpdart/example/src/function/identity.dart b/packages/fpdart/example/src/function/identity.dart deleted file mode 100644 index c8e44c2..0000000 --- a/packages/fpdart/example/src/function/identity.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -Future<void> main() async { - final either = Either<String, int>.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'); - - /// Using `identityFuture`/`idFuture`, the function just returns its input - /// parameter, wrapped in `Future.value`. - final withIdentityFuture = await either.match( - identityFuture, - (r) async => '$r', - ); -} diff --git a/packages/fpdart/example/src/io/overview.dart b/packages/fpdart/example/src/io/overview.dart deleted file mode 100644 index 070911d..0000000 --- a/packages/fpdart/example/src/io/overview.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -Future<void> main() async { - /// Create instance of [IO] from a value - final IO<int> io = IO.of(10); - - /// Create instance of [IO] from a sync function - final ioRun = IO(() => 10); - - /// Map [int] to [String] - final IO<String> map = io.map((a) => '$a'); - - /// Extract the value inside [IO] by running its function - final int value = io.run(); - - /// Chain another [IO] based on the value of the current [IO] - final flatMap = io.flatMap((a) => IO.of(a + 10)); -} diff --git a/packages/fpdart/example/src/list/fold.dart b/packages/fpdart/example/src/list/fold.dart deleted file mode 100644 index e53af71..0000000 --- a/packages/fpdart/example/src/list/fold.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -void main() { - const list = [1, 2, 3]; - - print('--- Visualize fold left'); - final fl = list.foldLeft<int>(0, (b, t) { - print("([$b] - [$t])"); - return b - t; - }); - print('== $fl'); - - print('--- Visualize fold right'); - final fr = list.foldRight<int>(0, (b, t) { - print("([$b] - [$t])"); - return b - t; - }); - print('== $fr'); - - print('--- Visualize fold left with index'); - final fli = list.foldLeftWithIndex<int>(0, (b, t, i) { - print("([$b] - [$t] - [$i])"); - return b - t - i; - }); - print('== $fli'); - - print('--- Visualize fold right with index'); - final fri = list.foldRightWithIndex<int>(0, (b, t, i) { - print("([$b] - [$t] - [$i])"); - return b - t - i; - }); - print('== $fri'); -} diff --git a/packages/fpdart/example/src/list/overview.dart b/packages/fpdart/example/src/list/overview.dart deleted file mode 100644 index 3d81121..0000000 --- a/packages/fpdart/example/src/list/overview.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -void main() { - /// Dart: `1` - [1, 2, 3, 4].first; - - /// fpdart: `Some(1)` - [1, 2, 3, 4].head; - - /// Dart: Throws a [StateError] ⚠️ - [].first; - - /// fpdart: `None()` - [].head; -} diff --git a/packages/fpdart/example/src/list/zip.dart b/packages/fpdart/example/src/list/zip.dart deleted file mode 100644 index 71031c7..0000000 --- a/packages/fpdart/example/src/list/zip.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -void main() { - final list1 = ['a', 'b']; - final list2 = [1, 2]; - final zipList = list1.zip(list2); - print(zipList); // -> [(a, 1), (b, 2)] -} diff --git a/packages/fpdart/example/src/map/overview.dart b/packages/fpdart/example/src/map/overview.dart deleted file mode 100644 index 05e21e8..0000000 --- a/packages/fpdart/example/src/map/overview.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -void main() { - final d1 = DateTime(2001, 1, 1); - final d2 = DateTime(2001, 1, 2); - - /// Use `eq` based on the `DateTime` year to upsert in the map. - /// - /// The first date `d1` will be overwritten by the second date `d2`, - /// since the year is the same. - final map = <DateTime, int>{} - .upsertAt( - Eq.dateEqYear, - d1, - 1, - ) - .upsertAt( - Eq.dateEqYear, - d2, - 2, - ); - - print(map); // {2001-01-02 00:00:00.000: 2} -} diff --git a/packages/fpdart/example/src/option/cheat_sheet.md b/packages/fpdart/example/src/option/cheat_sheet.md deleted file mode 100644 index b208bb6..0000000 --- a/packages/fpdart/example/src/option/cheat_sheet.md +++ /dev/null @@ -1,120 +0,0 @@ -# `Option` Cheat Sheet 👀 - -```dart -[_] -> None -[🍌] -> Some(🍌) - -🤷♂️ -> null -💥 -> Exception -``` - -## Constructors - -```dart -some(🍌) -> [🍌] -none() -> [_] - - 👆 same as 👇 - -Option.of(🍌) -> [🍌] -Option.none() -> [_] -``` - -```dart -optionOf(🤷♂️) -> [_] -optionOf(🍌) -> [🍌] - - 👆 same as 👇 - -Option.fromNullable(🤷♂️) -> [_] -Option.fromNullable(🍌) -> [🍌] -``` - -```dart -option(🍌, (b) => b == 🍌) -> [🍌] -option(🍌, (b) => b == 🍎) -> [_] - - 👆 same as 👇 - -Option.fromPredicate(🍌, (b) => b == 🍌) -> [🍌] -Option.fromPredicate(🍌, (b) => b == 🍎) -> [_] -``` - -```dart -Option.tryCatch(() => 🍌) -> [🍌] -Option.tryCatch(() => 💥) -> [_] -``` - -```dart -Option.flatten([ [🍌] ]) -> [🍌] -``` - -## Methods - -### `match` - -```dart -[🍌].match((🍌) => 🍌 * 2, () => 🍎) -> 🍌🍌 - -[_].match((🍌) => 🍌 * 2, () => 🍎) -> 🍎 -``` - -### `getOrElse` - -```dart -[🍌].getOrElse(() => 🍎) -> 🍌 - -[_].getOrElse(() => 🍎) -> 🍎 - - 👆 same as 👇 - -[🍌].match((🍌) => 🍌, () => 🍎) -``` - -### `map` - -```dart -[🥚].map((🥚) => 👨🍳(🥚)) -> [🍳] - -[_].map((🥚) => 👨🍳(🥚)) -> [_] -``` - -### `alt` - -```dart -[🍌].alt(() => [🍎]) -> [🍌] - -[_].alt(() => [🍎]) -> [🍎] -``` - -### `andThen` - -```dart -[🍌].andThen(() => [🍎]) -> [🍎] - -[_].andThen(() => [🍎]) -> [_] -``` - -### `flatMap` - -```dart -[😀].flatMap( - (😀) => [👻(😀)] - ) -> [😱] - -[😀].flatMap( - (😀) => [👻(😀)] - ).flatMap( - (😱) => [👨⚕️(😱)] - ) -> [🤕] - -[😀].flatMap( - (😀) => [_] - ).flatMap( - (_) => [👨⚕️(_)] - ) -> [_] - -[_].flatMap( - (😀) => [👻(😀)] - ) -> [_] -``` diff --git a/packages/fpdart/example/src/option/get-price/functional.dart b/packages/fpdart/example/src/option/get-price/functional.dart deleted file mode 100644 index 8932094..0000000 --- a/packages/fpdart/example/src/option/get-price/functional.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -Option<int> getPrice(String productName) { - if (productName.length > 6) { - return none(); - } - - return some(productName.length); -} - -void main() { - final price = getPrice('my product name'); - price.match( - () { - print('Sorry, no product found!'); - }, - (a) { - print('Total price is: $price'); - }, - ); -} diff --git a/packages/fpdart/example/src/option/get-price/non_functional.dart b/packages/fpdart/example/src/option/get-price/non_functional.dart deleted file mode 100644 index 1d1834e..0000000 --- a/packages/fpdart/example/src/option/get-price/non_functional.dart +++ /dev/null @@ -1,16 +0,0 @@ -int? getPrice(String productName) { - if (productName.length > 6) { - return null; - } - - return productName.length; -} - -void main() { - final price = getPrice('my product name'); - if (price == null) { - print('Sorry, no product found!'); - } else { - print('Total price is: $price'); - } -} diff --git a/packages/fpdart/example/src/option/nullable/option_nullable.dart b/packages/fpdart/example/src/option/nullable/option_nullable.dart deleted file mode 100644 index fef3765..0000000 --- a/packages/fpdart/example/src/option/nullable/option_nullable.dart +++ /dev/null @@ -1,64 +0,0 @@ -// ignore_for_file: unchecked_use_of_nullable_value, undefined_getter, unnecessary_null_comparison -import 'package:fpdart/fpdart.dart'; -import 'package:glados/glados.dart'; - -int doSomething(String str) => str.length + 10 * 2; -int doSomethingElse(int number) => number + 10 * 2; - -void main(List<String> args) { - int? nullableInt = 10; - if (nullableInt == null) { - print("Missing ‼️"); - } else { - print("Found $nullableInt 🎯"); - } - - /// 👆 Exactly the same as 👇 - - Option<int> optionInt = Option.of(10); - optionInt.match(() { - print("Missing ‼️"); - }, (t) { - print("Found $nullableInt 🎯"); - }); - - /// Null safety and `Option` save you from `null` 🚀 - String? str = Random().nextBool() ? "string" : null; - Option<String> optionStr = Random().nextBool() ? some("string") : none(); - - /// ⛔️ The property 'toLowerCase' can't be unconditionally accessed because the receiver can be 'null'. - str.toLowerCase; - - /// ⛔️ The getter 'toLowerCase' isn't defined for the type 'Option<String>'. - optionStr.toLowerCase; - - /// Option has methods that makes it more powerful (chain methods) ⛓ - String? strNullable = Random().nextBool() ? "string" : null; - Option<String> optionNullable = some("string"); - - /// Declarative API: more readable and composable 🎉 - Option<double> optionIntNullable = optionNullable - .map(doSomething) - .alt(() => some(20)) - .map(doSomethingElse) - .flatMap((t) => some(t / 2)); - - /// Not really clear what is going on here 🤔 - double? intNullable = (strNullable != null - ? doSomethingElse(doSomething(strNullable)) - : doSomethingElse(20)) / - 2; - - if (optionNullable.isSome()) { - /// Still type `Option<int>`, not `Some<int>` 😐 - optionIntNullable; - } - - if (strNullable != null) { - /// This is now `String` 🤝 - strNullable; - } - - List<int>? list = Random().nextBool() ? [1, 2, 3, 4] : null; - list.map((e) => /** What type is `e`? 😐 */ null); -} diff --git a/packages/fpdart/example/src/option/nullable/overview.dart b/packages/fpdart/example/src/option/nullable/overview.dart deleted file mode 100644 index 102d7c2..0000000 --- a/packages/fpdart/example/src/option/nullable/overview.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'dart:math'; - -import 'package:fpdart/fpdart.dart'; - -int? nullable() => Random().nextBool() ? 10 : null; - -String takesNullable(int? nullInt) => "$nullInt"; - -void main(List<String> args) { - int noNull = 10; - int? canBeNull = nullable(); - - /// `bool` - final noNullIsEven = noNull.isEven; - - /// final canBeNullIsEven = canBeNull.isEven; ⛔️ - - /// `bool?` - final canBeNullIsEven = canBeNull?.isEven; - - /// ☑️ - takesNullable(canBeNull); - - /// ☑️ - takesNullable(noNull); - - /// ☑️ - noNull.abs(); - - /// ☑️ - canBeNull?.abs(); - - Option<int> optionInt = Option.of(10); - int? nullInt = nullable(); - - nullInt?.abs(); - optionInt.map((t) => t.abs()); - - nullInt?.isEven; - optionInt.map((t) => t.isEven); - - takesNullable(nullInt); - - /// takesNullable(optionInt); ⛔️ - takesNullable(optionInt.toNullable()); -} diff --git a/packages/fpdart/example/src/option/option1.dart b/packages/fpdart/example/src/option/option1.dart deleted file mode 100644 index 65cc871..0000000 --- a/packages/fpdart/example/src/option/option1.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -void printString(String str) { - print(str); -} - -void main() { - final String? str = 'name'; - - /// Error: The argument type 'String?' can't be assigned to the parameter type 'String' - // printString(str); - - /// With dart null-safety, you must check that the value is not null - /// before calling the function - /// - /// What will happen if the value is `null` instead? - /// You are not required to handle such case, which can lead to errors! - if (str != null) { - printString(str); - } - - final Option<String> mStr = Option.of('name'); - - /// Using [Option] you are required to specify every possible case. - /// The type system helps you to find and define edge-cases and avoid errors. - mStr.match( - () => print('I have no string to print 🤷♀️'), - printString, - ); -} diff --git a/packages/fpdart/example/src/option/option2.dart b/packages/fpdart/example/src/option/option2.dart deleted file mode 100644 index a4f6d81..0000000 --- a/packages/fpdart/example/src/option/option2.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -double sumToDouble(int a, int b) => (a + b).toDouble(); - -void main() { - final a = Option.of(10); - final b = Option.of(20); - - /// `map` takes one parameter [int] and returns `sumToDouble`. - /// We therefore have a function inside a [Option] that we want to - /// apply to another value! - final Option<double Function(int)> map = a.map( - (a) => (int b) => sumToDouble(a, b), - ); - - /// Using `ap`, we get the final `Option<double>` that we want 🚀 - final result = b.ap(map); -} diff --git a/packages/fpdart/example/src/option/option3.dart b/packages/fpdart/example/src/option/option3.dart deleted file mode 100644 index 5e1637e..0000000 --- a/packages/fpdart/example/src/option/option3.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -int? stringToIntNull(String a) { - if (a.isNotEmpty) { - return a.length; - } else { - return null; - } -} - -double? intToDoubleNull(int a) { - if (a != 0) { - return a / 2; - } else { - return null; - } -} - -Option<int> stringToInt(String a) => Option.fromPredicateMap<String, int>( - a, - (str) => str.isNotEmpty, - (str) => str.length, - ); - -Option<double> intToDouble(int a) => - Option.fromPredicateMap<int, double>(a, (v) => v != 0, (v) => v / 2); - -void main() { - /// Using `null`, you are required to check that the value is not - /// `null` every time you call a function. - /// - /// Furthermore, you left unspecified what will happen when one of the - /// values is a `null` 🤦♂️ - const aNull = 'name'; - final intNull = stringToIntNull(aNull); - if (intNull != null) { - final doubleNull = intToDoubleNull(intNull); - } - - /// Using `flatMap`, you can forget that the value may be missing and just - /// use it as if it was there. - /// - /// In case one of the values is actually missing, you will get a [None] - /// at the end of the chain ⛓ - final a = Option.of('name'); - final Option<double> result = a.flatMap( - (s) => stringToInt(s).flatMap( - (i) => intToDouble(i), - ), - ); -} diff --git a/packages/fpdart/example/src/option/overview.dart b/packages/fpdart/example/src/option/overview.dart deleted file mode 100644 index c1e103f..0000000 --- a/packages/fpdart/example/src/option/overview.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -/// Don't do that! ⚠ -int divideI(int x, int y) => x ~/ y; // this will throw if y == 0 - -/// Error handling without exceptions using [Option] 🎉 -Option<int> divideF(int x, int y) { - if (y == 0) { - return none(); - } - return some(x ~/ y); -} - -/// Error handling with exceptions using [Option] 🎉 -Option<int> divide2F(int x, int y) => Option.tryCatch(() => x ~/ y); - -void main() { - // --- Initialize an Option 👇 --- // - const someInit = Some(10); - const noneInit = None(); - - final someInit2 = some(10); - final noneInit2 = none<int>(); - - /// Create an instance of [Some] - final option = Option.of(10); - - /// Create an instance of [None] - final noneInit3 = Option<int>.none(); - - /// If the predicate is `true`, then [Some], otherwise [None] - final predicate = Option<int>.fromPredicate(10, (a) => a > 5); - - /// If no exception, then [Some], otherwise [None] - final tryCatchInit = Option<int>.tryCatch(() => int.parse('10')); - - /// When the value is not `null`, then [Some], otherwise [None] - final nullable = Option<int>.fromNullable(10); - - /// Map [int] to [String] - final map = option.map((a) => '$a'); - - /// Extract the value from [Option] - final value = option.getOrElse(() => -1); - - /// Pattern matching - final match = option.match( - () => print('None'), - (a) => print('Some($a)'), - ); - - /// or use Dart's pattern matching as well 🤝 - final dartMatch = switch (option) { - None() => 'None', - Some(value: final a) => 'Some($a)', - }; - - /// Convert to [Either] - final either = option.toEither(() => 'missing'); - - /// Chain computations - final flatMap = option.flatMap((a) => Option.of(a + 10)); - - /// Return [None] if the function throws an error - final tryCatch = Option.tryCatch(() => int.parse('invalid')); -} diff --git a/packages/fpdart/example/src/option/shopping/functional.dart b/packages/fpdart/example/src/option/shopping/functional.dart deleted file mode 100644 index d6d1029..0000000 --- a/packages/fpdart/example/src/option/shopping/functional.dart +++ /dev/null @@ -1,83 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -class Market { - const Market(); - - // I want to buy a Banana, an Apple, and a Pear. If either one - // of these is missing, I will not but anything 😒 - Option<String> buyBanana() => getRandomOption('🍌'); - Option<String> buyApple() => getRandomOption('🍎'); - Option<String> buyPear() => getRandomOption('🍐'); - - Option<int> buyAmount() => getRandomOption(randomInt(1, 10).run()); -} - -Option<T> getRandomOption<T>(T value) => randomBool - .map( - (isValid) => isValid ? some(value) : none<T>(), - ) - .run(); - -// I go shopping in the Shopping Center. If it is closed, then -// I will go to the Local Market (which is always open 🥇). -Option<Market> goToShoppingCenter() => getRandomOption(const Market()); -Option<Market> goToLocalMarket() => some(const Market()); - -// Combine all the instructions and go shopping! 🛒 -String goShopping() => goToShoppingCenter() - .alt(goToLocalMarket) - .flatMap( - (market) => market.buyBanana().flatMap( - (banana) => market.buyApple().flatMap( - (apple) => market.buyPear().flatMap( - (pear) => Option.of('Shopping: $banana, $apple, $pear'), - ), - ), - ), - ) - .getOrElse( - () => 'I did not find 🍌 or 🍎 or 🍐, so I did not buy anything 🤷♂️', - ); - -// Combine all the instructions and go shopping! 🛒 -String goShoppingDo() => Option.Do( - (_) { - final market = _(goToShoppingCenter().alt(goToLocalMarket)); - final amount = _(market.buyAmount()); - - final banana = _(market.buyBanana()); - final apple = _(market.buyApple()); - final pear = _(market.buyPear()); - - return 'Shopping: $banana, $apple, $pear'; - }, - ).getOrElse( - () => 'I did not find 🍌 or 🍎 or 🍐, so I did not buy anything 🤷♂️', - ); - -// Combine all the instructions and go shopping! 🛒 -String goShoppingDoFlatMap() => goToShoppingCenter() - .alt(goToLocalMarket) - .flatMap( - (market) => Option.Do((_) { - final banana = _(market.buyBanana()); - final apple = _(market.buyApple()); - final pear = _(market.buyPear()); - return 'Shopping: $banana, $apple, $pear'; - }), - ) - .getOrElse( - () => 'I did not find 🍌 or 🍎 or 🍐, so I did not buy anything 🤷♂️', - ); - -void main() { - for (int i = 0; i < 100; i++) { - final shopping = goShopping(); - print(shopping); - } - - for (int i = 0; i < 100; i++) { - final shopping = goShoppingDo(); - print('[Do]: $shopping'); - } -} diff --git a/packages/fpdart/example/src/predicate/overview.dart b/packages/fpdart/example/src/predicate/overview.dart deleted file mode 100644 index af75530..0000000 --- a/packages/fpdart/example/src/predicate/overview.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -bool isEven(int n) => n % 2 == 0; -bool isDivisibleBy3(int n) => n % 3 == 0; - -final isOdd = isEven.negate; -final isEvenAndDivisibleBy3 = isEven.and(isDivisibleBy3); -final isEvenOrDivisibleBy3 = isEven.or(isDivisibleBy3); -final isStringWithEvenLength = isEven.contramap<String>((n) => n.length); diff --git a/packages/fpdart/example/src/pure_function.dart b/packages/fpdart/example/src/pure_function.dart deleted file mode 100644 index 7ba7c82..0000000 --- a/packages/fpdart/example/src/pure_function.dart +++ /dev/null @@ -1,41 +0,0 @@ -/// `Impure function` -/// -/// Modify input parameter -List<int> modifyInput(List<int> list) { - list.add(10); // <- Do not change input parameter 🙅♂️ - return list; -} - -var global = 10; - -/// `Impure function` -/// -/// Modify variable outside the scope of the function -int modifyGlobal(int a) { - global = global + a; // <- Do not change variable outside the function 🙅♂️ - return global; -} - -/// `Impure function` -/// -/// Side effects (Database, IO, API request) -int sideEffect(int a) { - print("Side effect"); // <- Do not introduce side effects 🙅♂️ - return global; -} - -/// `Impure function` -/// -/// Return `void`: Either the function does nothing, or a -/// side effect is guaranteed to be executed -void voidReturn(String str) { - print(str); // <- Either side effect, or nothing 🙅♂️ - return; -} - -/// `Impure function` -/// -/// Throw [Exception] (use [Option] or [Either] instead) -int throwException(int a) { - throw Exception(); // <- Do not throw 🙅♂️ -} diff --git a/packages/fpdart/example/src/reader/reader1.dart b/packages/fpdart/example/src/reader/reader1.dart deleted file mode 100644 index 6bb26e3..0000000 --- a/packages/fpdart/example/src/reader/reader1.dart +++ /dev/null @@ -1,51 +0,0 @@ -/// Source: https://gist.github.com/ruizb/554c17afb9cd3dedc76706862a9fa035 -import 'package:fpdart/src/reader.dart'; - -/// Dependency -abstract class Printer { - String write(String message); -} - -class BoldPrinter implements Printer { - @override - String write(String message) => '<b>$message</b>'; -} - -class ItalicPrinter implements Printer { - @override - String write(String message) => '<i>$message</i>'; -} - -/// Try 1: Supply the dependency every time you call the function -String printing1(String name, Printer printer) => printer.write(name); - -/// Try 2: Hide the dependency by curring -String Function(Printer) printing2(String name) => - (Printer printer) => printer.write(name); - -/// Try 3: Using the [Reader] monad to hide the dependency completely -Reader<Printer, String> printing3(String name) => Reader((r) => r.write(name)); - -void main() { - /// Required to pass [Printer] dependency, when all you would want is to - /// pass the `name` and get the result. - final String result1 = printing1('name', BoldPrinter()); - print(result1); // -> <b>name</b> - - /// Dependency on [Printer] hidden, but it is not possible to change - /// the result from `render2` after `printing2` has been called (for example using `map`). - final String Function(Printer) render2 = printing2('name'); - final String result2 = render2(BoldPrinter()); - print(result2); // -> <b>name</b> - - /// Dependency on [Printer] required only in the final call of `run`. - /// Before that you can change the value without bothering about the [Printer]. - final Reader<Printer, String> render3 = printing3('name'); - final Reader<Printer, int> map = render3.map((a) => a.length); - - /// Reader allows dependency injection - final String result3a = render3.run(BoldPrinter()); - final int result3b = map.run(ItalicPrinter()); - print(result3a); // -> <b>name</b> - print(result3b); // -> 11 -} diff --git a/packages/fpdart/example/src/reader/reader2.dart b/packages/fpdart/example/src/reader/reader2.dart deleted file mode 100644 index 90d9794..0000000 --- a/packages/fpdart/example/src/reader/reader2.dart +++ /dev/null @@ -1,49 +0,0 @@ -/// Source: https://gist.github.com/ruizb/554c17afb9cd3dedc76706862a9fa035 -import 'package:fpdart/src/reader.dart'; - -abstract class Dependency { - void logger(String message); - String get environment; -} - -class PrintLog implements Dependency { - @override - String get environment => 'Production'; - - @override - void logger(String message) { - print(message); - } -} - -/// Example 1: Without [Reader] -/// -/// We are required to pass [Dependency] between all the intermediate functions -/// (`b` and `a`), even if these functions do not use [Dependency]. Then just pass the -/// value to `c`. -int c(Dependency dependency) { - dependency.logger('Current environment: ${dependency.environment}'); - return 1; -} - -int b(Dependency dependency) => c(dependency) * 2; -int a(Dependency dependency) => b(dependency) + 1; - -/// Example 2: Using [Reader] -/// -/// Both `a` and `b` do not know about [Dependency]. The dependency is hidden -/// being the [Reader]. `a` and `b` just care about the value [int]. -Reader<Dependency, int> cReader() => Reader((dependency) { - dependency.logger('Current environment: ${dependency.environment}'); - return 1; - }); -Reader<Dependency, int> bReader() => cReader().map((a) => a * 2); -Reader<Dependency, int> aReader() => bReader().map((a) => a + 1); - -void main() { - final resultNoReader = a(PrintLog()); - print(resultNoReader); - - final resultWithReader = aReader().run(PrintLog()); - print(resultWithReader); -} diff --git a/packages/fpdart/example/src/reader_task_either/overview.dart b/packages/fpdart/example/src/reader_task_either/overview.dart deleted file mode 100644 index 1169079..0000000 --- a/packages/fpdart/example/src/reader_task_either/overview.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -typedef Env = (int, String); -typedef Error = String; -typedef Success = String; - -void main(List<String> args) async { - final rte = ReaderTaskEither<Env, Error, Success>.Do((_) async { - final a = 10; - final val = await _(ReaderTaskEither.fromReader( - Reader( - (env) => env.$1 + env.$2.length, - ), - )); - final env = await _(ReaderTaskEither.ask()); - final env2 = await _(ReaderTaskEither.asks((dep) => dep.$2)); - - return "$a and $val and $env and $env2"; - }); - - final result = await rte.run((30, "abc")); - print(result); -} diff --git a/packages/fpdart/example/src/state/state1.dart b/packages/fpdart/example/src/state/state1.dart deleted file mode 100644 index a007eaf..0000000 --- a/packages/fpdart/example/src/state/state1.dart +++ /dev/null @@ -1,57 +0,0 @@ -/// Source: http://www.learnyouahaskell.com/for-a-few-monads-more -import 'package:fpdart/src/state.dart'; -import 'package:fpdart/src/unit.dart'; - -/// [Stack] is an alias for [List<String>]. -typedef Stack = List<String>; - -const Stack stack = ['a', 'b', 'c']; - -/// Example Without State Monad -/// -/// We need to explicitly pass the state [Stack] every time we call `pop` or `push`. - -(String, Stack) pop(Stack s) => (s.last, s.sublist(0, s.length - 1)); - -(Unit, Stack) push(String value, Stack s) => (unit, [...s, value]); - -/// Example Using State Monad -/// -/// The global variable [Stack] is hidden using [State]. - -State<Stack, String> popState() => State( - (s) => (s.last, s.sublist(0, s.length - 1)), - ); - -State<Stack, Unit> pushState(String value) => State( - (s) => (unit, [...s, value]), - ); - -void main() { - // Without State Monad - final pop1NoState = pop(stack); - final push1NoState = push('d', pop1NoState.$2); - final pop2NoState = pop(push1NoState.$2); - print('No State'); - print(stack); - print('Pop'); - print(pop1NoState.$2); - print(pop1NoState.$1); - print("Push 'd'"); - print(push1NoState.$2); - print('Pop'); - print(pop2NoState.$2); - print(pop2NoState.$1); - - // Using State Monad - print('---'); - print('Using State'); - final withState = popState().execute( - pushState('d').execute( - popState().run(stack).$2, - ), - ); - final withState2 = popState()(pushState('d'))(popState()).run(stack); - print(withState); - print(withState2); -} diff --git a/packages/fpdart/example/src/state_async/state_async1.dart b/packages/fpdart/example/src/state_async/state_async1.dart deleted file mode 100644 index a44043c..0000000 --- a/packages/fpdart/example/src/state_async/state_async1.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -Future<int> add10(int previous) async => previous + 10; - -Future<void> main() async { - final stateAsync = StateAsync<int, Unit>( - (state) async => (unit, await add10(state)), - ); - - final result = await stateAsync.execute(10); - print(result); -} diff --git a/packages/fpdart/example/src/task/overview.dart b/packages/fpdart/example/src/task/overview.dart deleted file mode 100644 index 682885a..0000000 --- a/packages/fpdart/example/src/task/overview.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -/// You must run one [Future] after the other, no way around this... -Future<int> asyncI() { - return Future.value(10).then((value) => value * 10); -} - -/// No need of `async`, you decide when to run the [Future] ⚡ -Task<int> asyncF() { - return Task(() async => 10).map((a) => a * 10); -} - -Future<void> main() async { - /// Create instance of [Task] from a value - final Task<int> task = Task.of(10); - - /// Create instance of [Task] from an async function - final taskRun1 = Task(() async => 10); - final taskRun2 = Task(() => Future.value(10)); - - /// Map [int] to [String] - final Task<String> map = task.map((a) => '$a'); - - /// Extract the value inside [Task] by running its async function - final int value = await task.run(); - - /// Chain another [Task] based on the value of the current [Task] - final flatMap = task.flatMap((a) => Task.of(a + 10)); -} diff --git a/packages/fpdart/example/src/task/task_and_future.dart b/packages/fpdart/example/src/task/task_and_future.dart deleted file mode 100644 index 1678ced..0000000 --- a/packages/fpdart/example/src/task/task_and_future.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -/// Helper functions ⚙️ (sync) -String addNamePrefix(String name) => "Mr. $name"; -String addEmailPrefix(String email) => "mailto:$email"; -String decodeName(int code) => "$code"; - -/// API functions 🔌 (async) -Future<String> getUsername() => Future.value("Sandro"); -Future<int> getEncodedName() => Future.value(10); - -Future<String> getEmail() => Future.value("@"); - -Future<bool> sendInformation(String usernameOrName, String email) => - Future.value(true); - -Future<bool> withFuture() async { - late String usernameOrName; - late String email; - - try { - usernameOrName = await getUsername(); - } catch (e) { - try { - usernameOrName = decodeName(await getEncodedName()); - } catch (e) { - throw Exception("Missing both username and name"); - } - } - - try { - email = await getEmail(); - } catch (e) { - throw Exception("Missing email"); - } - - try { - final usernameOrNamePrefix = addNamePrefix(usernameOrName); - final emailPrefix = addEmailPrefix(email); - return await sendInformation(usernameOrNamePrefix, emailPrefix); - } catch (e) { - throw Exception("Error when sending information"); - } -} - -TaskEither<String, bool> withTask() => TaskEither.tryCatch( - getUsername, - (_, __) => "Missing username", - ) - .alt( - () => TaskEither.tryCatch( - getEncodedName, - (_, __) => "Missing name", - ).map( - decodeName, - ), - ) - .map( - addNamePrefix, - ) - .flatMap( - (usernameOrNamePrefix) => TaskEither.tryCatch( - getEmail, - (_, __) => "Missing email", - ) - .map( - addEmailPrefix, - ) - .flatMap( - (emailPrefix) => TaskEither.tryCatch( - () => sendInformation(usernameOrNamePrefix, emailPrefix), - (_, __) => "Error when sending information", - ), - ), - ); - -Task<int> getTask() => Task(() async { - print("I am running [Task]..."); - return 10; - }); - -Future<int> getFuture() async { - print("I am running [Future]..."); - return 10; -} - -void main() { - Task<int> taskInt = getTask(); - Future<int> futureInt = getFuture(); - - // Future<int> taskRun = taskInt.run(); -} diff --git a/packages/fpdart/example/src/task_either/async_flat_map/data.dart b/packages/fpdart/example/src/task_either/async_flat_map/data.dart deleted file mode 100644 index 8cb5115..0000000 --- a/packages/fpdart/example/src/task_either/async_flat_map/data.dart +++ /dev/null @@ -1,9 +0,0 @@ -class Student { - final String name; - Student(this.name); -} - -class Course { - final String name; - Course(this.name); -} diff --git a/packages/fpdart/example/src/task_either/async_flat_map/failure.dart b/packages/fpdart/example/src/task_either/async_flat_map/failure.dart deleted file mode 100644 index 402a745..0000000 --- a/packages/fpdart/example/src/task_either/async_flat_map/failure.dart +++ /dev/null @@ -1,5 +0,0 @@ -abstract class ApiFailure {} - -class StudentFailure implements ApiFailure {} - -class CourseFailure implements ApiFailure {} diff --git a/packages/fpdart/example/src/task_either/async_flat_map/main.dart b/packages/fpdart/example/src/task_either/async_flat_map/main.dart deleted file mode 100644 index c92fc1e..0000000 --- a/packages/fpdart/example/src/task_either/async_flat_map/main.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import 'data.dart'; -import 'failure.dart'; -import 'student_repo.dart'; - -TaskEither<ApiFailure, List<Student>> getStudents = TaskEither.tryCatch( - () => StudentRepo.getAllStudents(), - (_, __) => StudentFailure(), -); - -TaskEither<ApiFailure, List<Course>> getCoursesOfStudents( - List<Student> studentList, -) => - TaskEither.tryCatch( - () => StudentRepo.getAllCourses(studentList), - (_, __) => CourseFailure(), - ); - -String logFailure(ApiFailure apiFailure) { - if (apiFailure is StudentFailure) { - return 'Error while fetching list of students'; - } else if (apiFailure is CourseFailure) { - return 'Error while fetching list of courses'; - } else { - throw UnimplementedError(); - } -} - -void main() async { - /// How to call `getCoursesOfStudents` only if students is `Right`? - /// - /// Type: `TaskEither<ApiFailure, List<Course>>` - final taskEitherRequest = getStudents.flatMap(getCoursesOfStudents); - - /// In case of error map `ApiFailure` to `String` using `logFailure` - /// - /// Type: `TaskEither<String, List<Course>>` - final taskRequest = taskEitherRequest.mapLeft(logFailure); - - /// Run everything at the end! - /// - /// Type: `Either<String, List<Course>>` - final result = await taskRequest.run(); -} diff --git a/packages/fpdart/example/src/task_either/async_flat_map/student_repo.dart b/packages/fpdart/example/src/task_either/async_flat_map/student_repo.dart deleted file mode 100644 index 4de97f9..0000000 --- a/packages/fpdart/example/src/task_either/async_flat_map/student_repo.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'data.dart'; - -// ignore: avoid_classes_with_only_static_members -class StudentRepo { - static Future<List<Student>> getAllStudents() async => [ - Student("Juan"), - Student("Maria"), - ]; - - static Future<List<Course>> getAllCourses(List<Student> studentList) async => - [ - Course("Math"), - Course("Physics"), - ]; -} diff --git a/packages/fpdart/example/src/task_either/chain.dart b/packages/fpdart/example/src/task_either/chain.dart deleted file mode 100644 index 7bc7199..0000000 --- a/packages/fpdart/example/src/task_either/chain.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -typedef MessageResponse = String; -typedef AnalyticsResponse = int; - -TaskEither<String, MessageResponse> resendVerificationEmail = - TaskEither.of("done"); - -TaskEither<String, AnalyticsResponse> registerAnalytics = TaskEither.of(1); - -Future<void> main() async { - /** - * This will execute `resendVerificationEmail` - * - * If `resendVerificationEmail` is successful, then it will chain a call to `registerAnalytics` - * while still returning the result from `resendVerificationEmail` - */ - final taskEither = resendVerificationEmail.chainFirst( - (_) => registerAnalytics, - ); - - final result = await taskEither.run(); - print(result); // Right("done") -} diff --git a/packages/fpdart/example/src/task_either/finally.dart b/packages/fpdart/example/src/task_either/finally.dart deleted file mode 100644 index d1d92ce..0000000 --- a/packages/fpdart/example/src/task_either/finally.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -Future<int> apiRequestMock() => Future.value(10); - -/// Imperative code -/// -/// `try` - `catch` - `finally` -Future<void> imperative() async { - try { - final response = await apiRequestMock(); - print(response); - } catch (e) { - print("Error: $e"); - } finally { - print("Complete!"); - } -} - -/// Functional code -/// -/// `tryCatch` -Future<void> functional() async { - final task = TaskEither.tryCatch( - apiRequestMock, - (e, _) => "Error: $e", - ).match<Unit>( - (l) { - print(l); - return unit; - }, - (r) { - print(r); - return unit; - }, - ).chainFirst<Unit>( - (a) => Task( - () async { - print("Complete!"); - return unit; - }, - ), - ); - - task.run(); -} - -void main() { - imperative(); - functional(); -} diff --git a/packages/fpdart/example/src/task_either/overview.dart b/packages/fpdart/example/src/task_either/overview.dart deleted file mode 100644 index 5a7128d..0000000 --- a/packages/fpdart/example/src/task_either/overview.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -/// From [Future] to [TaskEither] -Future<int> imperative(String str) async { - try { - return int.parse(str); - } catch (e) { - return -1; // What does -1 means? 🤨 - } -} - -TaskEither<String, int> 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<int> asyncI() { - return Future<int>.error('Some error!') - .then((value) => value * 10) - .catchError( - (dynamic error) { - print(error); - return 0; - }, - ); -} - -/// Handle all the errors easily ✨ -TaskEither<String, int> asyncF() { - return TaskEither<String, int>( - () async => left('Some error'), - ).map((r) => r * 10); -} - -// Methods 👇 - -TaskEither<int, int> mapLeftExample(TaskEither<String, int> taskEither) => - taskEither.mapLeft( - (string) => string.length, - ); - -TaskEither<int, double> bimapExample(TaskEither<String, int> taskEither) => - taskEither.bimap( - (string) => string.length, - (number) => number / 2, - ); - -TaskEither<String, int> toTaskEitherExample(Either<String, int> taskEither) => - taskEither.toTaskEither(); - -/// Chain [Either] to [TaskEither] -TaskEither<String, int> binding = - TaskEither<String, String>.of("String").bindEither(Either.of(20)); diff --git a/packages/fpdart/example/src/task_either/sync_to_async.dart b/packages/fpdart/example/src/task_either/sync_to_async.dart deleted file mode 100644 index 15bee75..0000000 --- a/packages/fpdart/example/src/task_either/sync_to_async.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -Future<int> everythingIsFine(int a) async => a + 42; - -Future<String> sendComplainRequest(String a) async => - '$a - What data is that!!!'; - -Either<String, int> validate() => Either.of(10); - -void main() { - /// You have an [Either]. Now, suddenly a [Future] appears! - /// What do you do? - /// - /// You need to change the context, moving from a sync [Either] - /// to an async [TaskEither]! Simply use `toTaskEither`. - final eitherToTaskEither = validate() - .toTaskEither() - .flatMap( - (r) => TaskEither( - () async => Either.of( - await everythingIsFine(r), - ), - ), - ) - .orElse( - (l) => TaskEither( - () async => Either.left( - await sendComplainRequest(l), - ), - ), - ); -} diff --git a/packages/fpdart/example/src/task_option/future_task_option.dart b/packages/fpdart/example/src/task_option/future_task_option.dart deleted file mode 100644 index 62c8732..0000000 --- a/packages/fpdart/example/src/task_option/future_task_option.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -late Future<Map<String, Object>?> example; - -final taskOp = TaskOption.flatten( - (TaskOption.fromTask( - Task<Map<String, Object>?>( - () => example, - ).map( - (ex) => Option.fromNullable(ex).toTaskOption(), - ), - )), -); - -/// Using `Option.fromNullable`, the [Future] cannot fail -final taskOpNoFail = TaskOption<Map<String, Object>>( - () async => Option.fromNullable(await example), -); - -/// Using `Option.fromNullable` when the [Future] can fail -final taskOpFail = TaskOption<Map<String, Object>?>.tryCatch( - () => example, -).flatMap<Map<String, Object>>( - (r) => Option.fromNullable(r).toTaskOption(), -); diff --git a/packages/fpdart/example/src/task_option/overview.dart b/packages/fpdart/example/src/task_option/overview.dart deleted file mode 100644 index 26f9ebd..0000000 --- a/packages/fpdart/example/src/task_option/overview.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -// Methods 👇 - -TaskOption<int> toTaskOptionExample(Option<int> taskOption) => - taskOption.toTaskOption(); - -void main() {} diff --git a/packages/fpdart/example/src/traverse/option.dart b/packages/fpdart/example/src/traverse/option.dart deleted file mode 100644 index c3ed45c..0000000 --- a/packages/fpdart/example/src/traverse/option.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -void main() { - /// "a40" is invalid 💥 - final inputValues = ["10", "20", "30", "a40"]; - - /// Verify that all the values can be converted to [int] 🔐 - /// - /// If **any** of them is invalid, then the result is [None] 🙅♂️ - final traverseOption = inputValues.traverseOption( - (a) => Option.tryCatch( - /// If `a` does not contain a valid integer literal a [FormatException] is thrown - () => int.parse(a), - ), - ); - - print(traverseOption); -} diff --git a/packages/fpdart/example/src/traverse/sequnce_traverse.dart b/packages/fpdart/example/src/traverse/sequnce_traverse.dart deleted file mode 100644 index d063a8c..0000000 --- a/packages/fpdart/example/src/traverse/sequnce_traverse.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -void main() { - final inputValues = ["10", "20", "30", "40"]; - - /// Using `traverse` = `map` + `sequence` 🪄 - final traverseOption = inputValues.traverseOption( - (a) => Option.tryCatch(() => int.parse(a)), - ); - - /// Using `sequence`, same as the above with `traverse` 🪄 - final sequenceOption = inputValues - .map((a) => Option.tryCatch(() => int.parse(a))) - .sequenceOption(); - - /// `Some([10, 20, 30, 40])` - Same ☑️ - print(traverseOption); - - /// `Some([10, 20, 30, 40])` - Same ☑️ - print(sequenceOption); -} diff --git a/packages/fpdart/example/src/typeclass/eq/eq1.dart b/packages/fpdart/example/src/typeclass/eq/eq1.dart deleted file mode 100644 index d2488f2..0000000 --- a/packages/fpdart/example/src/typeclass/eq/eq1.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -class Parent { - final int value1; - final double value2; - const Parent(this.value1, this.value2); -} - -void main() { - /// Equality for values of type [Parent] based on their `value1` ([int]). - final eqParentInt = Eq.eqInt.contramap<Parent>( - (p) => p.value1, - ); - - /// Equality for of type [Parent] based on their `value2` ([double]). - final eqParentDouble = Eq.eqDouble.contramap<Parent>( - (p) => p.value2, - ); -} diff --git a/packages/fpdart/example/src/typeclass/order/order1.dart b/packages/fpdart/example/src/typeclass/order/order1.dart deleted file mode 100644 index a4b6e44..0000000 --- a/packages/fpdart/example/src/typeclass/order/order1.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -enum GreekLetter { alpha, beta, gama, delta } - -class _GreekLetterOrder extends Order<GreekLetter> { - const _GreekLetterOrder(); - - @override - int compare(GreekLetter a, GreekLetter b) => - _internalValue[a]! - _internalValue[b]!; - - static const _internalValue = <GreekLetter, int>{ - GreekLetter.alpha: 1, - GreekLetter.beta: 2, - GreekLetter.gama: 3, - GreekLetter.delta: 4, - }; -} - -const greekLetterOrder = _GreekLetterOrder(); - -void main() { - const letter1 = GreekLetter.alpha; - const letter2 = GreekLetter.beta; - - final compare1 = greekLetterOrder.compare(letter1, letter2); - print(compare1); // -> -1 (alpha < beta) - - final compare2 = greekLetterOrder.compare(letter1, letter1); - print(compare2); // -> 0 (alpha == alpha) - - final compare3 = greekLetterOrder.compare(letter2, letter1); - print(compare3); // -> 1 (beta > alpha) -} diff --git a/packages/fpdart/example/src/typeclass/order/order2.dart b/packages/fpdart/example/src/typeclass/order/order2.dart deleted file mode 100644 index d05a9c4..0000000 --- a/packages/fpdart/example/src/typeclass/order/order2.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -class Parent { - final int value1; - final double value2; - const Parent(this.value1, this.value2); -} - -void main() { - /// Order values of type [Parent] based on their `value1` ([int]). - final orderParentInt = Order.orderInt.contramap<Parent>( - (p) => p.value1, - ); - - /// Order values of type [Parent] based on their `value2` ([double]). - final orderParentDouble = Order.orderDouble.contramap<Parent>( - (p) => p.value2, - ); -} diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 376c05d..274bf44 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -14,7 +14,7 @@ final class _EffectThrow<L> { typedef DoAdapterEffect<E, L> = Future<A> Function<A>(IEffect<E, L, A>); -DoAdapterEffect<E, L> _doAdapter<E, L>(E env) => <A>(effect) => Future.sync( +DoAdapterEffect<E, L> _doAdapter<E, L>(E? env) => <A>(effect) => Future.sync( () => effect.asEffect._runEffect(env).then( (exit) => switch (exit) { Failure(value: final value) => throw _EffectThrow(value), @@ -31,7 +31,11 @@ abstract interface class IEffect<E, L, R> { } final class Effect<E, L, R> extends IEffect<E, L, R> { - final FutureOr<Exit<L, R>> Function(E env) _unsafeRun; + /// `E?` is optional to allow [Never] to work (`provideNever`). + /// + /// In practice a user of the library should never be allowed to pass `null` as [E]. + final FutureOr<Exit<L, R>> Function(E? env) _unsafeRun; + const Effect._(this._unsafeRun); @override @@ -43,7 +47,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { } /// {@category execution} - Future<Exit<L, R>> _runEffect(E env) async => _unsafeRun(env); + Future<Exit<L, R>> _runEffect(E? env) async => _unsafeRun(env); /// {@category execution} Future<Exit<L, R>> call(E env) => _runEffect(env); @@ -117,12 +121,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// /// {@category do_notation} Effect<V, L, R> provide<V>(E Function(V env) f) => Effect._( - (env) => _unsafeRun(f(env)), + (env) => _unsafeRun(f(env!)), ); /// {@category do_notation} static Effect<E, L, E> env<E, L>() => Effect._( - (env) async => Exit.success(env), + (env) async => Exit.success(env!), ); /// {@category combining} @@ -210,3 +214,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { }, ); } + +extension ProvideNever<L, R> on Effect<Never, L, R> { + /// Add a required dependency instead of [Never]. + /// + /// {@category do_notation} + Effect<V, L, R> withEnv<V>() => Effect._( + (env) => _unsafeRun(null), + ); +} diff --git a/packages/fpdart/test/src/band_test.dart b/packages/fpdart/test/src/band_test.dart deleted file mode 100644 index 98e93b5..0000000 --- a/packages/fpdart/test/src/band_test.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('Band', () { - group('is a', () { - final instance = Band.instance<int>((a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA<Semigroup>()); - }); - }); - - test('combineN', () { - final instance = Band.instance<int>((a1, a2) => a1 + a2); - expect(instance.combineN(1, 1), 1); - expect(instance.combineN(1, 10), 2); - }); - }); -} diff --git a/packages/fpdart/test/src/bounded_semilattice_test.dart b/packages/fpdart/test/src/bounded_semilattice_test.dart deleted file mode 100644 index c5b30c0..0000000 --- a/packages/fpdart/test/src/bounded_semilattice_test.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('BoundedSemilattice', () { - group('is a', () { - final instance = BoundedSemilattice.instance<int>(0, (a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA<Semigroup>()); - }); - - test('Band', () { - expect(instance, isA<Band>()); - }); - - test('Semilattice', () { - expect(instance, isA<Semilattice>()); - }); - - test('CommutativeSemigroup', () { - expect(instance, isA<CommutativeSemigroup>()); - }); - - test('Monoid', () { - expect(instance, isA<Monoid>()); - }); - - test('CommutativeMonoid', () { - expect(instance, isA<CommutativeMonoid>()); - }); - }); - - test('combineN', () { - final instance = BoundedSemilattice.instance<int>(0, (a1, a2) => a1 + a2); - expect(instance.combineN(1, 1), 1); - expect(instance.combineN(1, 10), 1); - }); - }); -} diff --git a/packages/fpdart/test/src/commutative_group_test.dart b/packages/fpdart/test/src/commutative_group_test.dart deleted file mode 100644 index a7163b6..0000000 --- a/packages/fpdart/test/src/commutative_group_test.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('CommutativeGroup', () { - group('is a', () { - final instance = - CommutativeGroup.instance<int>(0, (a1, a2) => a1 + a2, (a) => -a); - - test('Semigroup', () { - expect(instance, isA<Semigroup>()); - }); - - test('CommutativeSemigroup', () { - expect(instance, isA<CommutativeSemigroup>()); - }); - - test('CommutativeMonoid', () { - expect(instance, isA<CommutativeMonoid>()); - }); - - test('Monoid', () { - expect(instance, isA<Monoid>()); - }); - - test('Group', () { - expect(instance, isA<Group>()); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/commutative_monoid_test.dart b/packages/fpdart/test/src/commutative_monoid_test.dart deleted file mode 100644 index c077a62..0000000 --- a/packages/fpdart/test/src/commutative_monoid_test.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('CommutativeMonoid', () { - group('is a', () { - final instance = CommutativeMonoid.instance<int>(0, (a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA<Semigroup>()); - }); - - test('CommutativeSemigroup', () { - expect(instance, isA<CommutativeSemigroup>()); - }); - - test('Monoid', () { - expect(instance, isA<Monoid>()); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/commutative_semigroup_test.dart b/packages/fpdart/test/src/commutative_semigroup_test.dart deleted file mode 100644 index eb61501..0000000 --- a/packages/fpdart/test/src/commutative_semigroup_test.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('CommutativeSemigroup', () { - group('is a', () { - final instance = CommutativeSemigroup.instance<int>((a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA<Semigroup>()); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/date_test.dart b/packages/fpdart/test/src/date_test.dart deleted file mode 100644 index 5ec9684..0000000 --- a/packages/fpdart/test/src/date_test.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:glados/glados.dart'; - -void main() { - group('date', () { - group('[Property-based testing]', () { - Glados2<DateTime, DateTime>().test('dateOrder', (d1, d2) { - final compare = Order.orderDate.compare(d1, d2); - expect( - compare, - d1.isAfter(d2) - ? 1 - : d1.isBefore(d2) - ? -1 - : 0); - }); - - Glados2<DateTime, DateTime>().test('dateEqYear', (d1, d2) { - final compare = Eq.dateEqYear.eqv(d1, d2); - expect(compare, d1.year == d2.year); - }); - - Glados2<DateTime, DateTime>().test('dateEqMonth', (d1, d2) { - final compare = Eq.dateEqMonth.eqv(d1, d2); - expect(compare, d1.month == d2.month); - }); - - Glados2<DateTime, DateTime>().test('dateEqYearMonthDay', (d1, d2) { - final compare = Eq.dateEqYearMonthDay.eqv(d1, d2); - expect(compare, - d1.year == d2.year && d1.month == d2.month && d1.day == d2.day); - }); - - Glados2<DateTime, DateTime>().test('eqvYear', (d1, d2) { - final compare = d1.eqvYear(d2); - expect(compare, d1.year == d2.year); - }); - - Glados2<DateTime, DateTime>().test('eqvMonth', (d1, d2) { - final compare = d1.eqvMonth(d2); - expect(compare, d1.month == d2.month); - }); - - Glados2<DateTime, DateTime>().test('eqvDay', (d1, d2) { - final compare = d1.eqvDay(d2); - expect(compare, d1.day == d2.day); - }); - - Glados2<DateTime, DateTime>().test('eqvYearMonthDay', (d1, d2) { - final compare = d1.eqvYearMonthDay(d2); - expect(compare, - d1.year == d2.year && d1.month == d2.month && d1.day == d2.day); - }); - }); - - test('dateEqYear', () { - final date1 = DateTime(2021, 2, 2); - final date2 = DateTime(2021, 3, 3); - final date3 = DateTime(2020, 2, 2); - - expect(Eq.dateEqYear.eqv(date1, date1), true); - expect(Eq.dateEqYear.eqv(date1, date2), true); - expect(Eq.dateEqYear.eqv(date1, date3), false); - }); - - test('dateEqMonth', () { - final date1 = DateTime(2021, 2, 2); - final date2 = DateTime(2021, 3, 3); - final date3 = DateTime(2020, 2, 2); - - expect(Eq.dateEqMonth.eqv(date1, date1), true); - expect(Eq.dateEqMonth.eqv(date1, date2), false); - expect(Eq.dateEqMonth.eqv(date1, date3), true); - }); - - test('dateEqDay', () { - final date1 = DateTime(2021, 2, 2); - final date2 = DateTime(2021, 3, 3); - final date3 = DateTime(2020, 3, 2); - - expect(Eq.dateEqDay.eqv(date1, date1), true); - expect(Eq.dateEqDay.eqv(date1, date2), false); - expect(Eq.dateEqDay.eqv(date1, date3), true); - }); - - test('dateEqYearMonthDay', () { - final date1 = DateTime(2021, 2, 2, 10, 10); - final date2 = DateTime(2021, 2, 2, 11, 11); - final date3 = DateTime(2020, 2, 2, 12, 12); - - expect(Eq.dateEqYearMonthDay.eqv(date1, date1), true); - expect(Eq.dateEqYearMonthDay.eqv(date1, date2), true); - expect(Eq.dateEqYearMonthDay.eqv(date1, date3), false); - }); - }); -} diff --git a/packages/fpdart/test/src/either_test.dart b/packages/fpdart/test/src/either_test.dart deleted file mode 100644 index 2c23c5c..0000000 --- a/packages/fpdart/test/src/either_test.dart +++ /dev/null @@ -1,1221 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import './utils/utils.dart'; - -void main() { - group('Either', () { - group('[Property-based testing]', () { - group("safeCast", () { - Glados2(any.int, any.letterOrDigits) - .test('always returns Right without typed parameters', - (intValue, stringValue) { - final castInt = Either.safeCast(intValue, (value) => 'Error'); - final castString = Either.safeCast(stringValue, (value) => 'Error'); - expect(castInt, isA<Right<String, dynamic>>()); - expect(castString, isA<Right<String, dynamic>>()); - }); - }); - }); - - group('is a', () { - final either = Either<String, int>.of(10); - - test('Monad', () { - expect(either, isA<Monad2>()); - }); - - test('Applicative', () { - expect(either, isA<Applicative2>()); - }); - - test('Functor', () { - expect(either, isA<Functor2>()); - }); - - test('Foldable', () { - expect(either, isA<Foldable2>()); - }); - - test('Alt', () { - expect(either, isA<Alt2>()); - }); - - test('Extend', () { - expect(either, isA<Extend2>()); - }); - }); - - group('map', () { - test('Right', () { - final value = Either<String, int>.of(10); - final map = value.map((a) => a + 1); - map.matchTestRight((r) { - expect(r, 11); - }); - }); - - test('Left', () { - final value = Either<String, int>.left('abc'); - final map = value.map((a) => a + 1); - map.matchTestLeft((l) => expect(l, 'abc')); - }); - }); - - group('bimap', () { - test('Right', () { - final value = Either<String, int>.of(10); - final map = value.bimap((l) => "none", (a) => a + 1); - map.matchTestRight((r) { - expect(r, 11); - }); - }); - - test('Left', () { - final value = Either<String, int>.left('abc'); - final map = value.bimap((l) => "none", (a) => a + 1); - map.matchTestLeft((l) => expect(l, 'none')); - }); - }); - - group('map2', () { - test('Right', () { - final value = Either<String, int>.of(10); - final map = value.map2<double, double>( - Either<String, double>.of(1.5), (a, b) => a + b); - map.match((_) { - fail('should be right'); - }, (r) => expect(r, 11.5)); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final map = value.map2<double, double>( - Either<String, double>.of(1.5), (a, b) => a + b); - map.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('map3', () { - test('Right', () { - final value = Either<String, int>.of(10); - final map = value.map3<double, double, double>( - Either<String, double>.of(1.5), - Either<String, double>.of(1.5), - (a, b, c) => a + b + c); - map.match((_) { - fail('should be right'); - }, (r) => expect(r, 13.0)); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final map = value.map3<double, double, double>( - Either<String, double>.of(1.5), - Either<String, double>.of(1.5), - (a, b, c) => a + b + c); - map.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - test('pure', () { - final value = Either<String, int>.of(10); - final pure = value.pure('abc'); - pure.match((_) { - fail('should be right'); - }, (r) => expect(r, 'abc')); - }); - - group('mapLeft', () { - test('Right', () { - final value = Either<String, int>.of(10); - final map = value.mapLeft((a) => 'pre-$a'); - map.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final value = Either<String, int>.left('abc'); - final map = value.mapLeft((a) => 'pre-$a'); - map.match((l) => expect(l, 'pre-abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('foldRight', () { - test('Right', () { - final value = Either<String, int>.of(10); - final fold = value.foldRight<int>(10, (a, b) => a + b); - expect(fold, 20); - }); - - test('Left', () { - final value = Either<String, int>.left('abc'); - final fold = value.foldRight<int>(10, (a, b) => a + b); - expect(fold, 10); - }); - }); - - group('foldLeft', () { - test('Right', () { - final value = Either<String, int>.of(10); - final fold = value.foldLeft<int>(10, (a, b) => a + b); - expect(fold, 20); - }); - - test('Left', () { - final value = Either<String, int>.left('abc'); - final fold = value.foldLeft<int>(10, (a, b) => a + b); - expect(fold, 10); - }); - }); - - group('foldRightWithIndex', () { - test('Right', () { - final value = Either<String, int>.of(10); - final fold = value.foldRightWithIndex<int>(10, (i, a, b) => a + b); - expect(fold, 20); - }); - - test('Left', () { - final value = Either<String, int>.left('abc'); - final fold = value.foldRightWithIndex<int>(10, (i, a, b) => a + b); - expect(fold, 10); - }); - }); - - group('foldLeftWithIndex', () { - test('Right', () { - final value = Either<String, int>.of(10); - final fold = value.foldLeftWithIndex<int>(10, (i, a, b) => a + b); - expect(fold, 20); - }); - - test('Left', () { - final value = Either<String, int>.left('abc'); - final fold = value.foldLeftWithIndex<int>(10, (i, a, b) => a + b); - expect(fold, 10); - }); - }); - - group('foldMap', () { - test('Right', () { - final value = Either<String, int>.of(10); - final fold = value.foldMap<int>( - Monoid.instance(0, (a1, a2) => a1 + a2), (a) => a); - expect(fold, 10); - }); - - test('Left', () { - final value = Either<String, int>.left('abc'); - final fold = value.foldMap<int>( - Monoid.instance(0, (a1, a2) => a1 + a2), (a) => a); - expect(fold, 0); - }); - }); - - group('ap', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.ap(Either<String, int Function(int)>.of((n) => n + 1)); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 11)); - }); - - test('Left', () { - final value = Either<String, int>.of(10); - final ap = value.ap(Either<String, int Function(int)>.left('none')); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('alt', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.alt(() => Either<String, int>.of(0)); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.alt(() => Either<String, int>.of(0)); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 0)); - }); - }); - - group('extend', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.extend((t) => t.getOrElse((l) => -1) * 0.5); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 5.0)); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.extend((t) => t.getOrElse((l) => -1) * 0.5); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('duplicate', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.duplicate(); - expect(ap, isA<Either<String, Either<String, int>>>()); - ap.match((_) { - fail('should be right'); - }, - (r) => r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10))); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.duplicate(); - expect(ap, isA<Either<String, Either<String, int>>>()); - ap.match( - (l) => expect(l, 'none'), - (r) => r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - })); - }); - }); - - group('length', () { - test('Right', () { - final value = Either<String, int>.of(10); - expect(value.length(), 1); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - expect(value.length(), 0); - }); - }); - - group('concatenate', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.concatenate(Monoid.instance(0, (a1, a2) => a1 + a2)); - expect(ap, 10); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.concatenate(Monoid.instance(0, (a1, a2) => a1 + a2)); - expect(ap, 0); - }); - }); - - group('any', () { - test('Right (true)', () { - final value = Either<String, int>.of(10); - final ap = value.any((a) => a > 5); - expect(ap, true); - }); - - test('Right (false)', () { - final value = Either<String, int>.of(10); - final ap = value.any((a) => a < 5); - expect(ap, false); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.any((a) => a > 5); - expect(ap, false); - }); - }); - - group('all', () { - test('Right (true)', () { - final value = Either<String, int>.of(10); - final ap = value.all((a) => a > 5); - expect(ap, true); - }); - - test('Right (false)', () { - final value = Either<String, int>.of(10); - final ap = value.all((a) => a < 5); - expect(ap, false); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.all((a) => a > 5); - expect(ap, true); - }); - }); - - group('filterOrElse', () { - test('Right (true)', () { - final value = Either<String, int>.of(10); - final ap = value.filterOrElse((r) => r > 5, (r) => 'else'); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Right (false)', () { - final value = Either<String, int>.of(10); - final ap = value.filterOrElse((r) => r < 5, (r) => 'else'); - ap.match((l) => expect(l, 'else'), (_) { - fail('should be left'); - }); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.filterOrElse((r) => r > 5, (r) => 'else'); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('flatMap', () { - group('Right', () { - test('then Right', () { - final value = Either<String, int>.of(10); - final ap = value.flatMap<String>((a) => Right('$a')); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, '10')); - }); - - test('then Left', () { - final value = Either<String, int>.of(10); - final ap = - value.flatMap<String>((a) => Either<String, String>.left('none')); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('Left', () { - test('then Right', () { - final value = Either<String, int>.left('0'); - final ap = - value.flatMap<String>((a) => Either<String, String>.of('$a')); - ap.match((l) => expect(l, '0'), (_) { - fail('should be left'); - }); - }); - - test('then Left', () { - final value = Either<String, int>.left('0'); - final ap = - value.flatMap<String>((a) => Either<String, String>.left('none')); - ap.match((l) => expect(l, '0'), (_) { - fail('should be left'); - }); - }); - }); - }); - - group('toOption', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.toOption(); - ap.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.toOption(); - expect(ap, isA<None>()); - }); - }); - - group('toNullable', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.toNullable(); - expect(ap, 10); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.toNullable(); - expect(ap, null); - }); - }); - - group('toIOEither', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.toIOEither(); - final result = ap.run(); - expect(result, value); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.toIOEither(); - final result = ap.run(); - expect(result, value); - }); - }); - - group('toTaskEither', () { - test('Right', () async { - final value = Either<String, int>.of(10); - final ap = value.toTaskEither(); - final result = await ap.run(); - expect(result, value); - }); - - test('Left', () async { - final value = Either<String, int>.left('none'); - final ap = value.toTaskEither(); - final result = await ap.run(); - expect(result, value); - }); - }); - - group('isLeft', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.isLeft(); - expect(ap, false); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.isLeft(); - expect(ap, true); - }); - }); - - group('isRight', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.isRight(); - expect(ap, true); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.isRight(); - expect(ap, false); - }); - }); - - group('getLeft', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.getLeft(); - expect(ap, isA<None>()); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.getLeft(); - ap.matchTestSome((t) { - expect(t, 'none'); - }); - }); - }); - - group('getRight', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.getRight(); - ap.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.getRight(); - expect(ap, isA<None>()); - }); - }); - - group('getOrElse', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.getOrElse((l) => -1); - expect(ap, 10); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.getOrElse((l) => -1); - expect(ap, -1); - }); - }); - - group('match', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.match((l) => -1, (r) => 1); - expect(ap, 1); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.match((l) => -1, (r) => 1); - expect(ap, -1); - }); - }); - - group('elem', () { - test('Right (true)', () { - final value = Either<String, int>.of(10); - final ap = value.elem(10, Eq.instance((a1, a2) => a1 == a2)); - expect(ap, true); - }); - - test('Right (false)', () { - final value = Either<String, int>.of(10); - final ap = value.elem(0, Eq.instance((a1, a2) => a1 == a2)); - expect(ap, false); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.elem(10, Eq.instance((a1, a2) => a1 == a2)); - expect(ap, false); - }); - }); - - group('exists', () { - test('Right (true)', () { - final value = Either<String, int>.of(10); - final ap = value.exists((r) => r > 5); - expect(ap, true); - }); - - test('Right (false)', () { - final value = Either<String, int>.of(10); - final ap = value.exists((r) => r < 5); - expect(ap, false); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.exists((r) => r > 5); - expect(ap, false); - }); - }); - - group('swap', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.swap(); - ap.match((l) => expect(l, 10), (_) { - fail('should be left'); - }); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.swap(); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 'none')); - }); - }); - - group('flatten', () { - test('Right Right', () { - final value = Either<String, Either<String, int>>.of(Either.of(10)); - final ap = Either.flatten(value); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Right Left', () { - final value = - Either<String, Either<String, int>>.of(Either.left('none')); - final ap = Either.flatten(value); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('Left', () { - final value = Either<String, Either<String, int>>.left('none'); - final ap = Either.flatten(value); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('orElse', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.orElse((l) => Either<String, int>.of(0)); - ap.match((l) { - fail('should be right'); - }, (r) { - expect(r, 10); - }); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.orElse((l) => Either<String, int>.of(0)); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 0)); - }); - }); - - group('andThen', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value.andThen(() => Either<String, String>.of('10')); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, '10')); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value.andThen(() => Either<String, String>.of('10')); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('call', () { - test('Right', () { - final value = Either<String, int>.of(10); - final ap = value(Either<String, String>.of('10')); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, '10')); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - final ap = value(Either<String, String>.of('10')); - ap.match((l) { - expect(l, 'none'); - }, (_) { - fail('should be left'); - }); - }); - }); - - test('of()', () { - final value = Either<String, int>.of(10); - expect(value, isA<Right>()); - value.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('right()', () { - final value = Either<String, int>.right(10); - expect(value, isA<Right>()); - value.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('of() == right()', () { - final of = Either<String, int>.of(10); - final right = Either<String, int>.right(10); - expect(of, right); - }); - - test('left()', () { - final value = Either<String, int>.left('none'); - expect(value, isA<Left>()); - value.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - group('fromOption', () { - test('Some', () { - final value = Option.of(10); - final either = Either.fromOption(value, () => 'none'); - either.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('None', () { - final value = Option<int>.none(); - final either = Either.fromOption(value, () => 'none'); - either.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('fromPredicate', () { - test('Right', () { - final either = - Either<String, int>.fromPredicate(10, (v) => v > 5, (_) => 'none'); - either.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final either = - Either<String, int>.fromPredicate(10, (v) => v < 5, (_) => 'none'); - either.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('fromNullable', () { - test('Right', () { - final either = Either<String, int>.fromNullable(10, () => 'none'); - either.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final either = Either<String, int>.fromNullable(null, () => 'none'); - either.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('tryCatch', () { - test('Right', () { - final either = Either<String, int>.tryCatch( - () => int.parse('10'), (o, s) => 'none'); - either.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final either = Either<String, int>.tryCatch( - () => int.parse('invalid'), (o, s) => 'none'); - either.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('tryCatchK', () { - test('Right', () { - final either = Either<String, int>.of(10); - final ap = either.flatMap(Either.tryCatchK( - (n) => n + 5, - (_, __) => 'none', - )); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 15)); - }); - - test('Left', () { - final either = Either<String, int>.of(10); - final ap = either.flatMap(Either.tryCatchK( - (_) => int.parse('invalid'), - (_, __) => 'none', - )); - ap.match((l) => expect(l, 'none'), (r) { - fail('should be left'); - }); - }); - }); - - test('getEq', () { - final eq = Either.getEq<String, int>( - Eq.instance((a1, a2) => a1 == a2), Eq.instance((a1, a2) => a1 == a2)); - final eitherR = Either<String, int>.of(10); - final eitherL = Either<String, int>.left('none'); - expect(eq.eqv(eitherR, eitherR), true); - expect(eq.eqv(eitherR, Either<String, int>.of(10)), true); - expect(eq.eqv(eitherR, Either<String, int>.of(9)), false); - expect(eq.eqv(eitherR, Either<String, int>.left('none')), false); - expect(eq.eqv(eitherL, eitherL), true); - expect(eq.eqv(eitherL, Either<String, int>.left('none')), true); - expect(eq.eqv(eitherL, Either<String, int>.left('error')), false); - }); - - test('getSemigroup', () { - final sg = Either.getSemigroup<String, int>( - Semigroup.instance((a1, a2) => a1 + a2)); - final eitherR = Either<String, int>.of(10); - final eitherL = Either<String, int>.left('none'); - expect(sg.combine(eitherR, eitherR), Either<String, int>.of(20)); - expect(sg.combine(eitherR, eitherL), eitherR); - expect(sg.combine(eitherL, eitherR), eitherR); - expect(sg.combine(eitherL, Either<String, int>.left('error')), eitherL); - }); - - test('Right value', () { - const r = Right<String, int>(10); - expect(r.value, 10); - }); - - test('Left value', () { - const l = Left<String, int>('none'); - expect(l.value, 'none'); - }); - - group('sequenceList', () { - test('Right', () { - final list = [right(1), right(2), right(3), right(4)]; - final result = Either.sequenceList(list); - result.matchTestRight((r) { - expect(r, [1, 2, 3, 4]); - }); - }); - - test('Left', () { - final list = [ - right<String, int>(1), - left<String, int>("Error"), - right<String, int>(3), - right<String, int>(4) - ]; - final result = Either.sequenceList(list); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('traverseList', () { - test('Right', () { - final list = [1, 2, 3, 4, 5, 6]; - final result = - Either.traverseList<String, int, String>(list, (a) => right("$a")); - result.matchTestRight((r) { - expect(r, ["1", "2", "3", "4", "5", "6"]); - }); - }); - - test('Left', () { - final list = [1, 2, 3, 4, 5, 6]; - final result = Either.traverseList<String, int, String>( - list, - (a) => a % 2 == 0 ? right("$a") : left("Error"), - ); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('traverseListWithIndex', () { - test('Right', () { - final list = [1, 2, 3, 4, 5, 6]; - final result = Either.traverseListWithIndex<String, int, String>( - list, (a, i) => right("$a$i")); - result.matchTestRight((r) { - expect(r, ["10", "21", "32", "43", "54", "65"]); - }); - }); - - test('Left', () { - final list = [1, 2, 3, 4, 5, 6]; - final result = Either.traverseListWithIndex<String, int, String>( - list, - (a, i) => i % 2 == 0 ? right("$a$i") : left("Error"), - ); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - test('Right == Right', () { - final r1 = Either<String, int>.of(10); - final r2 = Either<String, int>.of(9); - final r3 = Either<String, double>.of(8.0); - final r4 = Either<String, int>.of(10); - final r5 = Either<String, double>.of(10.0); - final l1 = Either<String, int>.left('none'); - final l2 = Either<String, int>.left('error'); - final map1 = <String, Either>{'m1': r1, 'm2': r1}; - final map2 = <String, Either>{'m1': r1, 'm2': r2}; - final map3 = <String, Either>{'m1': r1, 'm2': r4}; - final map4 = <String, Either>{'m1': r1, 'm2': r3}; - final map5 = <String, Either>{'m1': r1, 'm2': r5}; - final map6 = <String, Either>{'m1': r1, 'm2': r1}; - final map7 = <String, Either>{'m1': r1, 'm2': l1}; - expect(r1, r1); - expect(r1, r4); - expect(r1, r5); - expect(r1 == r2, false); - expect(r1 == r3, false); - expect(r1 == r3, false); - expect(r1 == l1, false); - expect(r1 == l2, false); - expect(map1, map1); - expect(map1, map6); - expect(map1, map3); - expect(map1, map5); - expect(map1 == map2, false); - expect(map1 == map4, false); - expect(map1 == map7, false); - }); - - test('Left == Left', () { - final r1 = Either<String, int>.of(10); - final l1 = Either<String, int>.left('none'); - final l2 = Either<String, int>.left('error'); - final l3 = Either<String, int>.left('none'); - final l4 = Either<double, int>.left(1.0); - final map1 = <String, Either>{'m1': l1, 'm2': l1}; - final map2 = <String, Either>{'m1': l1, 'm2': l3}; - final map3 = <String, Either>{'m1': l1, 'm2': l2}; - final map4 = <String, Either>{'m1': l1, 'm2': l4}; - final map5 = <String, Either>{'m1': l1, 'm2': r1}; - expect(l1, l1); - expect(l1, l3); - expect(l1 == l2, false); - expect(l1 == r1, false); - expect(map1, map1); - expect(map1, map2); - expect(map1 == map3, false); - expect(map1 == map4, false); - expect(map1 == map5, false); - }); - - group('toString', () { - test('Right', () { - final value = Either<String, int>.of(10); - expect(value.toString(), 'Right(10)'); - }); - - test('Left', () { - final value = Either<String, int>.left('none'); - expect(value.toString(), 'Left(none)'); - }); - }); - }); - - group('bind', () { - test('Right', () { - final either1 = Either<String, int>.of(10); - final result = either1.bind((r) => Either<String, int>.of(r + 10)); - expect(result.getOrElse((l) => 0), 20); - }); - - test('Left', () { - final either1 = Either<String, int>.left('String'); - final result = either1.bind((r) => Either<String, int>.of(r + 10)); - expect(result.getOrElse((l) => 0), 0); - expect(result.getLeft().getOrElse(() => ''), 'String'); - }); - }); - - group('bindFuture', () { - test('Right', () async { - final either1 = Either<String, int>.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<String, int>.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'); - }); - }); - - test('chainFirst', () { - final either = Either<String, int>.of(10); - var sideEffect = 10; - final chain = either.chainFirst((b) { - sideEffect = 100; - return Either.left("abc"); - }); - chain.match( - (l) => fail('should be right'), - (r) { - expect(r, 10); - expect(sideEffect, 100); - }, - ); - }); - - test('rights', () { - final list = [ - right<String, int>(1), - right<String, int>(2), - left<String, int>('a'), - left<String, int>('b'), - right<String, int>(3), - ]; - final result = Either.rights(list); - expect(result, [1, 2, 3]); - }); - - test('lefts', () { - final list = [ - right<String, int>(1), - right<String, int>(2), - left<String, int>('a'), - left<String, int>('b'), - right<String, int>(3), - ]; - final result = Either.lefts(list); - expect(result, ['a', 'b']); - }); - - test('partitionEithers', () { - final list = [ - right<String, int>(1), - right<String, int>(2), - left<String, int>('a'), - left<String, int>('b'), - right<String, int>(3), - ]; - final result = Either.partitionEithers(list); - expect(result.$1, ['a', 'b']); - expect(result.$2, [1, 2, 3]); - }); - - group('safeCast', () { - test('dynamic', () { - final castInt = Either.safeCast(10, (value) => 'Error'); - final castString = Either.safeCast('abc', (value) => 'Error'); - expect(castInt, isA<Right<String, dynamic>>()); - expect(castString, isA<Right<String, dynamic>>()); - }); - - test('Right', () { - final cast = Either<String, int>.safeCast(10, (value) => 'Error'); - cast.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () { - final cast = Either<String, int>.safeCast('abc', (value) => 'Error'); - cast.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('safeCastStrict', () { - test('Right', () { - final cast = - Either.safeCastStrict<String, int, int>(10, (value) => 'Error'); - cast.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () { - final cast = - Either.safeCastStrict<String, int, String>('abc', (value) => 'Error'); - cast.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('Do Notation', () { - test('should return the correct value', () { - final doEither = Either.Do((_) => _(Either.of(10))); - doEither.matchTestRight((t) { - expect(t, 10); - }); - }); - - test('should extract the correct values', () { - final doEither = Either.Do((_) { - final a = _(Either.of(10)); - final b = _(Either.of(5)); - return a + b; - }); - doEither.matchTestRight((t) { - expect(t, 15); - }); - }); - - test('should return Left if any Either is Left', () { - final doEither = Either<String, int>.Do((_) { - final a = _(Either.of(10)); - final b = _(Either.of(5)); - final c = _(Either<String, int>.left('Error')); - return a + b + c; - }); - doEither.matchTestLeft((t) { - expect(t, 'Error'); - }); - }); - - test('should rethrow if throw is used inside Do', () { - final doEither = () => Either<String, int>.Do((_) { - _(Either.of(10)); - throw UnimplementedError(); - }); - - expect(doEither, throwsA(const TypeMatcher<UnimplementedError>())); - }); - - test('should rethrow if Left is thrown inside Do', () { - final doEither = () => Either<String, int>.Do((_) { - _(Either.of(10)); - throw Left('Error'); - }); - - expect(doEither, throwsA(const TypeMatcher<Left>())); - }); - - test('should no execute past the first Left', () { - var mutable = 10; - final doEitherLeft = Either<String, int>.Do((_) { - final a = _(Either.of(10)); - final b = _(Either<String, int>.left("Error")); - mutable += 10; - return a + b; - }); - - expect(mutable, 10); - doEitherLeft.matchTestLeft((l) { - expect(l, "Error"); - }); - - final doEitherRight = Either<String, int>.Do((_) { - final a = _(Either.of(10)); - mutable += 10; - return a; - }); - - expect(mutable, 20); - doEitherRight.matchTestRight((t) { - expect(t, 10); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/eq_test.dart b/packages/fpdart/test/src/eq_test.dart deleted file mode 100644 index 4ed1b94..0000000 --- a/packages/fpdart/test/src/eq_test.dart +++ /dev/null @@ -1,188 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -class _Parent { - final int value1; - final double value2; - const _Parent(this.value1, this.value2); -} - -void main() { - group('Eq', () { - test('.instance (int)', () { - final instance = Eq.instance<int>((a1, a2) => a1 == (a2 + 1)); - - // eqv - expect(instance.eqv(1, 1), false); - expect(instance.eqv(2, 1), true); - expect(instance.eqv(3, 1), false); - - // neqv - expect(instance.neqv(1, 1), true); - expect(instance.neqv(2, 1), false); - expect(instance.neqv(3, 1), true); - }); - - test('.instance (String)', () { - final instance = Eq.instance<String>( - (a1, a2) => a1.substring(0, 2) == a2.substring(0, 2)); - expect(instance.eqv('abc', 'abc'), true); - expect(instance.eqv('abc', 'acb'), false); - }); - - test('and', () { - final instance1 = Eq.instance<String>( - (a1, a2) => a1.substring(0, 2) == a2.substring(0, 2)); - final instance2 = Eq.instance<String>( - (a1, a2) => a1.substring(2, 4) == a2.substring(2, 4)); - final and = instance1.and(instance2); - expect(instance1.eqv('abef', 'abcd'), true); - expect(instance2.eqv('abef', 'zxef'), true); - expect(and.eqv('abcd', 'abcd'), true); - expect(and.eqv('abdc', 'abcd'), false); - expect(and.eqv('bacd', 'abcd'), false); - }); - - test('or', () { - final instance1 = Eq.instance<int>((a1, a2) => a1 == (a2 + 2)); - final instance2 = Eq.instance<int>((a1, a2) => a1 == (a2 + 3)); - final or = instance1.or(instance2); - expect(or.eqv(2, 1), false); - expect(or.eqv(3, 1), true); - expect(or.eqv(4, 1), true); - expect(or.eqv(5, 1), false); - }); - - test('xor', () { - final instance1 = Eq.instance<int>((a1, a2) => a1 == (a2 + 2)); - final instance2 = Eq.instance<int>((a1, a2) => a1 == (a2 + 3)); - final xor = instance1.xor(instance2); - final xorSame = instance1.xor(instance1); - expect(xor.eqv(2, 1), false); - expect(xor.eqv(3, 1), true); - expect(xor.eqv(4, 1), true); - expect(xorSame.eqv(3, 1), false); - }); - - test('.fromUniversalEquals', () { - final instance = Eq.fromUniversalEquals<int>(); - expect(instance.eqv(1, 1), true); - expect(instance.eqv(1, 2), false); - }); - - test('.allEqual', () { - final instance = Eq.allEqual<int>(); - expect(instance.eqv(1, 1), true); - expect(instance.eqv(1, 2), true); - expect(instance.eqv(2, 1), true); - }); - - test('.by', () { - final instance = Eq.instance<int>((a1, a2) => a1 == a2); - final by = Eq.by<String, int>((a) => a.length, instance); - expect(by.eqv('abc', 'abc'), true); - expect(by.eqv('abc', 'ab'), false); - }); - - test('.eqNum', () { - final eq = Eq.eqNum; - expect(eq.eqv(10, 10), true); - expect(eq.eqv(10.0, 10), true); - expect(eq.eqv(10.5, 10.5), true); - expect(eq.eqv(-10, -10.0), true); - expect(eq.eqv(10, 10.5), false); - }); - - test('.eqInt', () { - final eq = Eq.eqInt; - expect(eq.eqv(10, 10), true); - expect(eq.eqv(11, 10), false); - expect(eq.eqv(-10, -10), true); - expect(eq.eqv(10, 11), false); - }); - - test('.eqDouble', () { - final eq = Eq.eqDouble; - expect(eq.eqv(10, 10), true); - expect(eq.eqv(10.0, 10), true); - expect(eq.eqv(10.5, 10.5), true); - expect(eq.eqv(-10, -10.0), true); - expect(eq.eqv(10, 10.5), false); - }); - - test('.eqString', () { - final eq = Eq.eqString; - expect(eq.eqv("abc", "abc"), true); - expect(eq.eqv("abc", "abd"), false); - expect(eq.eqv("abc", "ab"), false); - expect(eq.eqv("a", "a"), true); - expect(eq.eqv("a", "ab"), false); - }); - - test('.eqBool', () { - final eq = Eq.eqBool; - expect(eq.eqv(true, true), true); - expect(eq.eqv(false, true), false); - expect(eq.eqv(true, false), false); - expect(eq.eqv(false, false), true); - }); - - group('contramap', () { - test('int', () { - final eqParentInt = Eq.eqInt.contramap<_Parent>( - (p) => p.value1, - ); - - expect( - eqParentInt.eqv( - _Parent(1, 2.5), - _Parent(1, 12.5), - ), - true, - ); - expect( - eqParentInt.eqv( - _Parent(1, 2.5), - _Parent(4, 2.5), - ), - false, - ); - expect( - eqParentInt.eqv( - _Parent(-1, 2.5), - _Parent(1, 12.5), - ), - false, - ); - }); - - test('double', () { - final eqParentDouble = Eq.eqDouble.contramap<_Parent>( - (p) => p.value2, - ); - - expect( - eqParentDouble.eqv( - _Parent(1, 2.5), - _Parent(1, 2.5), - ), - true, - ); - expect( - eqParentDouble.eqv( - _Parent(1, 2.5), - _Parent(1, 12.5), - ), - false, - ); - expect( - eqParentDouble.eqv( - _Parent(-1, 2.5), - _Parent(1, 2), - ), - false, - ); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/extension/curry_extension_test.dart b/packages/fpdart/test/src/extension/curry_extension_test.dart deleted file mode 100644 index 5175225..0000000 --- a/packages/fpdart/test/src/extension/curry_extension_test.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:glados/glados.dart'; - -void main() { - group('Curry/Uncarry extension', () { - group('Curry/Uncarry (2)', () { - int subtract(int n1, int n2) => n1 - n2; - int Function(int) subtractCurried(int n1) => (n2) => n1 - n2; - - Glados2<int, int>(any.int, any.int).test('curry', (n1, n2) { - expect(subtract(n1, n2), subtract.curry(n1)(n2)); - }); - - Glados2<int, int>(any.int, any.int).test('curryLast', (n1, n2) { - expect(subtract(n1, n2), subtract.curryLast(n2)(n1)); - }); - - Glados2<int, int>(any.int, any.int).test('uncurry', (n1, n2) { - expect(subtractCurried(n1)(n2), subtractCurried.uncurry(n1, n2)); - }); - }); - - group('Curry/Uncarry (3)', () { - int subtract(int n1, int n2, int n3) => n1 - n2 - n3; - int Function(int) Function(int) subtractCurriedAll(int n1) => - (n2) => (n3) => n1 - n2 - n3; - - Glados3<int, int, int>(any.int, any.int, any.int).test('curry', - (n1, n2, n3) { - expect(subtract(n1, n2, n3), subtract.curry(n1)(n2, n3)); - }); - - Glados3<int, int, int>(any.int, any.int, any.int).test('curryLast', - (n1, n2, n3) { - expect(subtract(n1, n2, n3), subtract.curryLast(n3)(n1, n2)); - }); - - Glados3<int, int, int>(any.int, any.int, any.int).test('curryAll', - (n1, n2, n3) { - expect(subtract(n1, n2, n3), subtract.curryAll(n1)(n2)(n3)); - }); - - Glados3<int, int, int>(any.int, any.int, any.int).test('uncurry', - (n1, n2, n3) { - expect(subtractCurriedAll(n1)(n2)(n3), - subtractCurriedAll.uncurry(n1, n2, n3)); - }); - }); - - group('Curry/Uncarry (4)', () { - int subtract(int n1, int n2, int n3, int n4) => n1 - n2 - n3 - n4; - int Function(int) Function(int) Function(int) subtractCurriedAll( - int n1) => - (n2) => (n3) => (n4) => n1 - n2 - n3 - n4; - - Glados3<int, int, int>(any.int, any.int, any.int).test('curry', - (n1, n2, n3) { - expect(subtract(n1, n2, n3, n1), subtract.curry(n1)(n2, n3, n1)); - }); - - Glados3<int, int, int>(any.int, any.int, any.int).test('curryLast', - (n1, n2, n3) { - expect(subtract(n1, n2, n3, n2), subtract.curryLast(n2)(n1, n2, n3)); - }); - - Glados3<int, int, int>(any.int, any.int, any.int).test('curryAll', - (n1, n2, n3) { - expect(subtract(n1, n2, n3, n1), subtract.curryAll(n1)(n2)(n3)(n1)); - }); - - Glados3<int, int, int>(any.int, any.int, any.int).test('uncurry', - (n1, n2, n3) { - expect(subtractCurriedAll(n1)(n2)(n3)(n1), - subtractCurriedAll.uncurry(n1, n2, n3, n1)); - }); - }); - - group('Curry/Uncarry (5)', () { - int subtract(int n1, int n2, int n3, int n4, int n5) => - n1 - n2 - n3 - n4 - n5; - int Function(int) Function(int) Function(int) Function(int) - subtractCurriedAll(int n1) => - (n2) => (n3) => (n4) => (n5) => n1 - n2 - n3 - n4 - n5; - - Glados3<int, int, int>(any.int, any.int, any.int).test('curry', - (n1, n2, n3) { - expect( - subtract(n1, n2, n3, n1, n2), subtract.curry(n1)(n2, n3, n1, n2)); - }); - - Glados3<int, int, int>(any.int, any.int, any.int).test('curryLast', - (n1, n2, n3) { - expect(subtract(n1, n2, n3, n1, n3), - subtract.curryLast(n3)(n1, n2, n3, n1)); - }); - - Glados3<int, int, int>(any.int, any.int, any.int).test('curryAll', - (n1, n2, n3) { - expect(subtract(n1, n2, n3, n1, n2), - subtract.curryAll(n1)(n2)(n3)(n1)(n2)); - }); - - Glados3<int, int, int>(any.int, any.int, any.int).test('uncurry', - (n1, n2, n3) { - expect(subtractCurriedAll(n1)(n2)(n3)(n1)(n2), - subtractCurriedAll.uncurry(n1, n2, n3, n1, n2)); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/extension/date_time_extension_test.dart b/packages/fpdart/test/src/extension/date_time_extension_test.dart deleted file mode 100644 index dc53951..0000000 --- a/packages/fpdart/test/src/extension/date_time_extension_test.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import '../utils/utils.dart'; - -void main() { - group('FpdartOnDateTime', () { - group('[Property-based testing]', () { - Glados2<DateTime, DateTime>().test('eqvYear == dateEqYear', (d1, d2) { - expect(d1.eqvYear(d2), Eq.dateEqYear.eqv(d1, d2)); - }); - - Glados2<DateTime, DateTime>().test('eqvMonth == dateEqMonth', (d1, d2) { - expect(d1.eqvMonth(d2), Eq.dateEqMonth.eqv(d1, d2)); - }); - - Glados2<DateTime, DateTime>().test('eqvDay == dateEqDay', (d1, d2) { - expect(d1.eqvDay(d2), Eq.dateEqDay.eqv(d1, d2)); - }); - - Glados2<DateTime, DateTime>().test('eqvYearMonthDay == dateEqYear', - (d1, d2) { - expect(d1.eqvYearMonthDay(d2), Eq.dateEqYear.eqv(d1, d2)); - }); - }); - - test('eqvYear', () { - final date1 = DateTime(2021, 2, 2); - final date2 = DateTime(2021, 3, 3); - final date3 = DateTime(2020, 2, 2); - - expect(date1.eqvYear(date1), true); - expect(date1.eqvYear(date2), true); - expect(date1.eqvYear(date3), false); - }); - - test('eqvMonth', () { - final date1 = DateTime(2021, 2, 2); - final date2 = DateTime(2021, 3, 3); - final date3 = DateTime(2020, 2, 2); - - expect(date1.eqvMonth(date1), true); - expect(date1.eqvMonth(date2), false); - expect(date1.eqvMonth(date3), true); - }); - - test('eqvDay', () { - final date1 = DateTime(2021, 2, 2); - final date2 = DateTime(2021, 3, 3); - final date3 = DateTime(2020, 3, 2); - - expect(date1.eqvDay(date1), true); - expect(date1.eqvDay(date2), false); - expect(date1.eqvDay(date3), true); - }); - - test('eqvYearMonthDay', () { - final date1 = DateTime(2021, 2, 2, 10, 10); - final date2 = DateTime(2021, 2, 2, 11, 11); - final date3 = DateTime(2020, 2, 2, 12, 12); - - expect(date1.eqvYearMonthDay(date1), true); - expect(date1.eqvYearMonthDay(date2), true); - expect(date1.eqvYearMonthDay(date3), false); - }); - }); -} diff --git a/packages/fpdart/test/src/extension/iterable_extension_test.dart b/packages/fpdart/test/src/extension/iterable_extension_test.dart deleted file mode 100644 index 27cd7de..0000000 --- a/packages/fpdart/test/src/extension/iterable_extension_test.dart +++ /dev/null @@ -1,1674 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import '../utils/utils.dart'; - -/// Used to test sorting with [DateTime] (`sortWithDate`) -class SortDate { - final int id; - final DateTime date; - const SortDate(this.id, this.date); -} - -void main() { - /// Check if two [Iterable] have the same element in the same order - bool eq<T>(Iterable<T> a, Iterable<T> b) => a.foldLeftWithIndex( - false, - (a, e, i) => e == b.elementAt(i), - ); - - group('FpdartOnList', () { - test('zipWith', () { - final list1 = [1, 2]; - final list2 = ['a', 'b']; - final result = list1.zipWith<String, double>( - (t, i) => (t + i.length) / 2, - list2, - ); - - expect(eq(result, [1.0, 1.5]), true); - }); - - test('zip', () { - final list1 = [1, 2]; - final list2 = ['a', 'b']; - final ap = list1.zip(list2); - - expect(eq(ap, [(1, 'a'), (2, 'b')]), true); - }); - - test('filter', () { - final list1 = [1, 2, 3, 4, 5, 6]; - final ap = list1.filter((t) => t > 3); - - expect(eq(ap, [4, 5, 6]), true); - }); - - test('filterWithIndex', () { - final list1 = [0, 1, 2, 3, 4, 5, 6]; - final ap = list1.filterWithIndex((t, index) => t > 3 && index < 6); - - expect(eq(ap, [4, 5]), true); - }); - - test('concat', () { - final list1 = [1, 2, 3, 4, 5, 6]; - final ap = list1.concat([7, 8]); - - expect(eq(ap, [1, 2, 3, 4, 5, 6, 7, 8]), true); - }); - - test('append', () { - final list1 = [1, 2, 3, 4, 5, 6]; - final ap = list1.append(7); - - expect(eq(ap, [1, 2, 3, 4, 5, 6, 7]), true); - }); - - test('prepend', () { - final list1 = [1, 2, 3, 4, 5, 6]; - final ap = list1.prepend(0); - - expect(eq(ap, [0, 1, 2, 3, 4, 5, 6]), true); - }); - - test('prependAll', () { - final list1 = [1, 2, 3, 4, 5, 6]; - final ap = list1.prependAll([10, 11, 12]); - - expect(eq(ap, [10, 11, 12, 1, 2, 3, 4, 5, 6]), true); - }); - - test('insertBy', () { - final list1 = [1, 2, 3, 4, 5, 6]; - final ap = list1.insertBy(Order.from((a1, a2) => a1.compareTo(a2)), 4); - - expect(eq(ap, [1, 2, 3, 4, 4, 5, 6]), true); - }); - - test('insertWith', () { - final list1 = [ - SortDate(2, DateTime(2019)), - SortDate(4, DateTime(2017)), - SortDate(1, DateTime(2020)), - SortDate(3, DateTime(2018)), - ]; - final ap = list1.insertWith( - (instance) => instance.date, - Order.orderDate, - SortDate(5, DateTime(2021)), - ); - - expect(ap.elementAt(4).id, 5); - expect(ap.elementAt(4).date.year, 2021); - }); - - test('sortBy', () { - final list1 = [2, 6, 4, 1, 5, 3]; - final ap = list1.sortBy(Order.from((a1, a2) => a1.compareTo(a2))); - - expect(eq(ap, [1, 2, 3, 4, 5, 6]), true); - }); - - test('sortWith', () { - final list1 = [ - SortDate(2, DateTime(2019)), - SortDate(4, DateTime(2017)), - SortDate(1, DateTime(2020)), - SortDate(3, DateTime(2018)), - ]; - final ap = list1.sortWith((instance) => instance.date, Order.orderDate); - - expect(ap.elementAt(0).id, 4); - expect(ap.elementAt(1).id, 3); - expect(ap.elementAt(2).id, 2); - expect(ap.elementAt(3).id, 1); - }); - - test('sortWithDate', () { - final list1 = [ - SortDate(2, DateTime(2019)), - SortDate(4, DateTime(2017)), - SortDate(1, DateTime(2020)), - SortDate(3, DateTime(2018)), - ]; - final ap = list1.sortWithDate((instance) => instance.date); - - expect(ap.elementAt(0).date.year, 2017); - expect(ap.elementAt(1).date.year, 2018); - expect(ap.elementAt(2).date.year, 2019); - expect(ap.elementAt(3).date.year, 2020); - }); - - test('sortBy', () { - final list1 = [2, 6, 4, 1, 5, 3]; - final ap = list1.sortBy(Order.from((a1, a2) => a1.compareTo(a2))); - - expect(eq(ap, [1, 2, 3, 4, 5, 6]), true); - }); - - test('intersect', () { - final list1 = [1, 2, 3, 4, 5, 6]; - final ap = list1.intersect([1, 2, 3, 10, 11, 12]); - - expect(eq(ap, [1, 2, 3]), true); - }); - - test('difference', () { - final list1 = [1, 2, 3]; - final ap = list1.difference( - Eq.instance<int>((a1, a2) => a1 == a2), - [2, 3, 4], - ); - - expect(eq(ap, [1]), true); - }); - - test('intersperse', () { - final ap = [1, 2, 3].intersperse(10); - - expect(eq(ap, [1, 10, 2, 10, 3]), true); - }); - - group('head', () { - test('Some', () { - final list1 = [1, 2]; - final ap = list1.head; - expect(ap, isA<Some>()); - expect(ap.getOrElse(() => -1), 1); - }); - - test('None', () { - final List<int> list1 = []; - final ap = list1.head; - expect(ap, isA<None>()); - expect(ap.getOrElse(() => -1), -1); - }); - }); - - group('firstOption', () { - test('Some', () { - final list1 = [1, 2]; - final ap = list1.firstOption; - expect(ap, isA<Some>()); - expect(ap.getOrElse(() => -1), 1); - }); - }); - - group('tail', () { - test('Some', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.tail; - expect(ap, isA<Some>()); - expect(ap.getOrElse(() => []), [2, 3, 4]); - }); - }); - - group('init', () { - test('Some', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.init; - expect(ap, isA<Some>()); - expect(ap.getOrElse(() => []), [1, 2, 3]); - }); - }); - - group('lastOption', () { - test('Some', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.lastOption; - expect(ap, isA<Some>()); - expect(ap.getOrElse(() => -1), 4); - }); - }); - - test('takeWhileLeft', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.takeWhileLeft((t) => t < 3); - expect(eq(ap, [1, 2]), true); - }); - - test('dropWhileLeft', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.dropWhileLeft((t) => t < 3); - expect(eq(ap, [3, 4]), true); - }); - - test('span', () { - final list1 = [1, 5, 2, 3, 4]; - final ap = list1.span((t) => t < 3); - expect(ap.$1.length, 1); - expect(ap.$1.elementAt(0), 1); - - expect(ap.$2.length, 4); - expect(ap.$2.elementAt(0), 5); - expect(ap.$2.elementAt(1), 2); - expect(ap.$2.elementAt(2), 3); - expect(ap.$2.elementAt(3), 4); - }); - - test('breakI', () { - final list1 = [4, 5, 1, 3, 4]; - final ap = list1.breakI((t) => t < 3); - - expect(ap.$1.length, 2); - expect(ap.$1.elementAt(0), 4); - expect(ap.$1.elementAt(1), 5); - - expect(ap.$2.length, 3); - expect(ap.$2.elementAt(0), 1); - expect(ap.$2.elementAt(1), 3); - expect(ap.$2.elementAt(2), 4); - }); - - test('splitAt', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.splitAt(2); - expect(eq(ap.$1, [1, 2]), true); - expect(eq(ap.$2, [3, 4]), true); - }); - - test('delete', () { - final list1 = [1, 2, 3, 2]; - final ap = list1.delete(2); - expect(ap.length, 3); - expect(ap.elementAt(0), 1); - expect(ap.elementAt(1), 3); - expect(ap.elementAt(2), 2); - }); - - test('maximumBy', () { - final list1 = [2, 5, 4, 6, 1, 3]; - final ap = list1.maximumBy(Order.from((a1, a2) => a1.compareTo(a2))); - expect(ap.getOrElse(() => -1), 6); - }); - - test('minimumBy', () { - final list1 = [2, 5, 4, 6, 1, 3]; - final ap = list1.minimumBy(Order.from((a1, a2) => a1.compareTo(a2))); - expect(ap.getOrElse(() => -1), 1); - }); - - test('drop', () { - final list1 = [1, 2, 3, 4, 5]; - final ap = list1.drop(2); - expect(eq(ap, [3, 4, 5]), true); - }); - - group('dropRight', () { - test('none', () { - expect([].dropRight(0), isEmpty); - expect([].dropRight(1), isEmpty); - expect([].dropRight(2), isEmpty); - expect([1].dropRight(1), isEmpty); - expect([1, 2].dropRight(2), isEmpty); - expect([1, 2].dropRight(3), isEmpty); - }); - - test('some', () { - expect([1, 2, 3, 4].dropRight(0), [1, 2, 3, 4]); - expect([1, 2, 3, 4].dropRight(1), [1, 2, 3]); - expect([1, 2, 3, 4].dropRight(2), [1, 2]); - expect([1, 2, 3, 4].dropRight(3), [1]); - // Is lazy. - var list = [1, 2, 3]; - var dropList = list.dropRight(5); - list.addAll([4, 5, 6]); - expect(dropList, [1]); - }); - }); - - test('foldLeft', () { - final list1 = [1, 2, 3]; - final ap = list1.foldLeft<int>(0, (b, t) => b - t); - expect(ap, -6); - }); - - test('foldLeftWithIndex', () { - final list1 = [1, 2, 3]; - final ap = list1.foldLeftWithIndex<int>(0, (b, t, i) => b - t - i); - expect(ap, -9); - }); - - test('mapWithIndex', () { - final list1 = [1, 2, 3]; - final ap = list1.mapWithIndex<String>((t, index) => '$t$index'); - expect(eq(ap, ['10', '21', '32']), true); - }); - - test('flatMap', () { - final list1 = [1, 2, 3]; - final ap = list1.flatMap((t) => [t, t + 1]); - expect(eq(ap, [1, 2, 2, 3, 3, 4]), true); - }); - - test('flatMapWithIndex', () { - final list1 = [1, 2, 3]; - final ap = list1.flatMapWithIndex((t, i) => [t, t + i]); - expect(eq(ap, [1, 1, 2, 3, 3, 5]), true); - }); - - test('ap', () { - final list1 = [1, 2, 3]; - final ap = list1.ap([(a) => a + 1, (a) => a + 2]); - expect(eq(ap, [2, 3, 3, 4, 4, 5]), true); - }); - - test('partition', () { - final list1 = [2, 4, 5, 6, 1, 3]; - final ap = list1.partition((t) => t > 2); - - expect(ap.$1.length, 2); - expect(ap.$1.elementAt(0), 2); - expect(ap.$1.elementAt(1), 1); - - expect(ap.$2.length, 4); - expect(ap.$2.elementAt(0), 4); - expect(ap.$2.elementAt(1), 5); - expect(ap.$2.elementAt(2), 6); - expect(ap.$2.elementAt(3), 3); - }); - - group('all', () { - test('true', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.all((t) => t < 5); - expect(ap, true); - }); - - test('false', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.all((t) => t < 4); - expect(ap, false); - }); - }); - - group('elem', () { - test('true', () { - final list1 = [1, 2, 3, 4]; - final ap1 = list1.elem(1); - final ap2 = list1.elem(2); - final ap3 = list1.elem(3); - final ap4 = list1.elem(4); - expect(ap1, true); - expect(ap2, true); - expect(ap3, true); - expect(ap4, true); - }); - - test('false', () { - final list1 = [1, 2, 3, 4]; - final ap1 = list1.elem(-1); - final ap2 = list1.elem(0); - final ap3 = list1.elem(5); - final ap4 = list1.elem(6); - expect(ap1, false); - expect(ap2, false); - expect(ap3, false); - expect(ap4, false); - }); - }); - - group('notElem', () { - test('false', () { - final list1 = [1, 2, 3, 4]; - final ap1 = list1.notElem(1); - final ap2 = list1.notElem(2); - final ap3 = list1.notElem(3); - final ap4 = list1.notElem(4); - expect(ap1, false); - expect(ap2, false); - expect(ap3, false); - expect(ap4, false); - }); - - test('true', () { - final list1 = [1, 2, 3, 4]; - final ap1 = list1.notElem(-1); - final ap2 = list1.notElem(0); - final ap3 = list1.notElem(5); - final ap4 = list1.notElem(6); - expect(ap1, true); - expect(ap2, true); - expect(ap3, true); - expect(ap4, true); - }); - }); - - group('lookupEq', () { - test('none', () { - expect([].lookupEq(Eq.eqInt, 5), isA<None>()); - }); - - test('none found', () { - expect([1, 2, 3, 4].lookupEq(Eq.eqInt, 5), isA<None>()); - }); - - test('found', () { - var find3 = [1, 2, 3, 4].lookupEq(Eq.eqInt, 3); - expect(find3, isA<Some>()); - expect(find3.getOrElse(() => throw "not"), 3); - }); - - test('found first', () { - var findMod3 = - [1, 6, 4, 3, 2].lookupEq(Eq.by((n) => n % 3, Eq.eqInt), 0); - expect(findMod3, isA<Some>()); - expect(findMod3.getOrElse(() => throw "not"), 6); - }); - }); - }); - - group('FpdartOnMutableIterableOfIterable', () { - test('concat', () { - final list1 = [ - [1, 2], - [2, 3], - [3, 4] - ]; - final ap = list1.flatten; - - expect(eq(ap, [1, 2, 2, 3, 3, 4]), true); - }); - }); - - group('FpdartTraversableIterable', () { - group('traverseOption', () { - test('Some', () { - final list = [1, 2, 3, 4]; - final result = list.traverseOption(some); - result.matchTestSome((t) { - expect(list, t); - }); - }); - - test('None', () { - final list = [1, 2, 3, 4]; - final result = - list.traverseOption<int>((t) => t == 3 ? none() : some(t)); - expect(result, isA<None>()); - }); - }); - - group('traverseOptionWithIndex', () { - test('Some', () { - final list = [1, 2, 3, 4]; - final result = list.traverseOptionWithIndex((a, i) => some(a + i)); - result.matchTestSome((t) { - expect(t, [1, 3, 5, 7]); - }); - }); - - test('None', () { - final list = [1, 2, 3, 4]; - final result = list.traverseOptionWithIndex<int>( - (a, i) => i == 3 ? none() : some(a + i)); - expect(result, isA<None>()); - }); - }); - - group('traverseEither', () { - test('Right', () { - final list = [1, 2, 3, 4]; - final result = list.traverseEither(right); - result.matchTestRight((t) { - expect(list, t); - }); - }); - - test('Left', () { - final list = [1, 2, 3, 4]; - final result = - list.traverseEither((t) => t == 3 ? left("Error") : right(t)); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('traverseEitherWithIndex', () { - test('Right', () { - final list = [1, 2, 3, 4]; - final result = list.traverseEitherWithIndex((a, i) => right(a + i)); - result.matchTestRight((t) { - expect(t, [1, 3, 5, 7]); - }); - }); - - test('Left', () { - final list = [1, 2, 3, 4]; - final result = list.traverseEitherWithIndex( - (a, i) => i == 3 ? left("Error") : right(a + i)); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('traverseIOEither', () { - test('Right', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseIOEither<String, String>((a) { - sideEffect += 1; - return IOEither.of("$a"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestRight((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, list.length); - }); - - test('Left', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseIOEither<String, String>((a) { - sideEffect += 1; - return a % 2 == 0 ? IOEither.left("Error") : IOEither.of("$a"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - - group('traverseIOEitherWithIndex', () { - test('Right', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseIOEitherWithIndex<String, String>((a, i) { - sideEffect += 1; - return IOEither.of("$a$i"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestRight((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, list.length); - }); - - test('Left', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseIOEitherWithIndex<String, String>((a, i) { - sideEffect += 1; - return a % 2 == 0 ? IOEither.left("Error") : IOEither.of("$a$i"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - - test('traverseIO', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseIO<String>((a) { - sideEffect += 1; - return IO.of("$a"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, ['1', '2', '3', '4', '5', '6']); - expect(sideEffect, list.length); - }); - - test('traverseIOWithIndex', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseIOWithIndex<String>((a, i) { - sideEffect += 1; - return IO.of("$a$i"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, ['10', '21', '32', '43', '54', '65']); - expect(sideEffect, list.length); - }); - - test('traverseTask', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTask<String>( - (a) => Task( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return "$a"; - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, ['1', '2', '3', '4', '5', '6']); - expect(sideEffect, list.length); - }); - - test('traverseTaskWithIndex', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskWithIndex<String>( - (a, i) => Task( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return "$a$i"; - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, ['10', '21', '32', '43', '54', '65']); - expect(sideEffect, list.length); - }); - - test('traverseTaskSeq', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskSeq<String>( - (a) => Task( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a; - return "$a"; - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, ['1', '2', '3', '4', '5', '6']); - expect(sideEffect, 6); - }); - - test('traverseTaskWithIndexSeq', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskWithIndexSeq<String>( - (a, i) => Task( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return "$a$i"; - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, ['10', '21', '32', '43', '54', '65']); - expect(sideEffect, 11); - }); - - group('traverseIOOption', () { - test('Some', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseIOOption<String>( - (a) => IOOption( - () { - sideEffect += 1; - return some("$a"); - }, - ), - ); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestSome((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, list.length); - }); - - test('None', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseIOOption<String>( - (a) => IOOption( - () { - sideEffect += 1; - return a % 2 == 0 ? some("$a") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - - group('traverseTaskOptionWithIndex', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionWithIndex<String>( - (a, i) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some("$a$i"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, list.length); - }); - - test('None', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionWithIndex<String>( - (a, i) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 ? some("$a$i") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - - group('traverseTaskOption', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOption<String>( - (a) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some("$a"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, list.length); - }); - - test('None', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOption<String>( - (a) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 ? some("$a") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - - group('traverseTaskOptionWithIndex', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionWithIndex<String>( - (a, i) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some("$a$i"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, list.length); - }); - - test('None', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionWithIndex<String>( - (a, i) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 ? some("$a$i") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - - group('traverseTaskOptionSeq', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionSeq<String>( - (a) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return some("$a"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, 5); - }); - - test('None', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionSeq<String>( - (a) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return a % 2 == 0 ? some("$a") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, 5); - }); - }); - - group('traverseTaskOptionWithIndexSeq', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionWithIndexSeq<String>( - (a, i) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return some("$a$i"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, 11); - }); - - test('None', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionWithIndexSeq<String>( - (a, i) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return a % 2 == 0 ? some("$a$i") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, 11); - }); - }); - - group('traverseTaskEither', () { - test('Right', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskEither<String, String>( - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right<String, String>("$a"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, list.length); - }); - - test('Left', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskEither<String, String>( - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 - ? right<String, String>("$a") - : left<String, String>("Error"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - - group('traverseTaskEitherWithIndex', () { - test('Right', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskEitherWithIndex<String, String>( - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right<String, String>("$a$i"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, list.length); - }); - - test('Left', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskEitherWithIndex<String, String>( - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 - ? right<String, String>("$a$i") - : left<String, String>("Error"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - - group('traverseTaskEitherSeq', () { - test('Right', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskEitherSeq<String, String>( - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return right<String, String>("$a"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, 5); - }); - - test('Left', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskEitherSeq<String, String>( - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return a % 2 == 0 - ? right<String, String>("$a") - : left<String, String>("Error"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, 5); - }); - }); - - group('traverseTaskEitherWithIndexSeq', () { - test('Right', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskEitherWithIndexSeq<String, String>( - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return right<String, String>("$a$i"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, 11); - }); - - test('Left', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskEitherWithIndexSeq<String, String>( - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return a % 2 == 0 - ? right<String, String>("$a$i") - : left<String, String>("Error"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, 11); - }); - }); - }); - - group('FpdartSequenceIterableOption', () { - group('sequenceOption', () { - test('Some', () { - final list = [some(1), some(2), some(3), some(4)]; - final result = list.sequenceOption(); - result.matchTestSome((t) { - expect(t, [1, 2, 3, 4]); - }); - }); - - test('None', () { - final list = [some(1), none<int>(), some(3), some(4)]; - final result = list.sequenceOption(); - expect(result, isA<None>()); - }); - }); - }); - - group('FpdartSequenceIterableIO', () { - test('sequenceIO', () { - var sideEffect = 0; - final list = [ - IO(() { - sideEffect += 1; - return 1; - }), - IO(() { - sideEffect += 1; - return 2; - }), - IO(() { - sideEffect += 1; - return 3; - }), - IO(() { - sideEffect += 1; - return 4; - }) - ]; - final traverse = list.sequenceIO(); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, [1, 2, 3, 4]); - expect(sideEffect, list.length); - }); - }); - - group('FpdartSequenceIterableTask', () { - test('sequenceTask', () async { - var sideEffect = 0; - final list = [ - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return 1; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return 2; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return 3; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return 4; - }), - ]; - final traverse = list.sequenceTask(); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, [1, 2, 3, 4]); - expect(sideEffect, list.length); - }); - - test('sequenceTaskSeq', () async { - var sideEffect = 0; - final list = [ - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect = 0; - return 1; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return 2; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return 3; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return 4; - }), - ]; - final traverse = list.sequenceTaskSeq(); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, [1, 2, 3, 4]); - expect(sideEffect, 3); - }); - }); - - group('FpdartSequenceIterableEither', () { - group('sequenceEither', () { - test('Right', () { - final list = [right(1), right(2), right(3), right(4)]; - final result = list.sequenceEither(); - result.matchTestRight((r) { - expect(r, [1, 2, 3, 4]); - }); - }); - - test('Left', () { - final list = [ - right<String, int>(1), - left<String, int>("Error"), - right<String, int>(3), - right<String, int>(4) - ]; - final result = list.sequenceEither(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - test('rightsEither', () { - final list = [ - right<String, int>(1), - right<String, int>(2), - left<String, int>('a'), - left<String, int>('b'), - right<String, int>(3), - ]; - final result = list.rightsEither(); - expect(result, [1, 2, 3]); - }); - - test('leftsEither', () { - final list = [ - right<String, int>(1), - right<String, int>(2), - left<String, int>('a'), - left<String, int>('b'), - right<String, int>(3), - ]; - final result = list.leftsEither(); - expect(result, ['a', 'b']); - }); - - test('partitionEithersEither', () { - final list = [ - right<String, int>(1), - right<String, int>(2), - left<String, int>('a'), - left<String, int>('b'), - right<String, int>(3), - ]; - final result = list.partitionEithersEither(); - expect(result.$1, ['a', 'b']); - expect(result.$2, [1, 2, 3]); - }); - }); - - group('FpdartSequenceIterableIOOption', () { - group('sequenceIOOption', () { - test('Some', () { - var sideEffect = 0; - final list = [ - IOOption(() { - sideEffect += 1; - return some(1); - }), - IOOption(() { - sideEffect += 1; - return some(2); - }), - IOOption(() { - sideEffect += 1; - return some(3); - }), - IOOption(() { - sideEffect += 1; - return some(4); - }), - ]; - final traverse = list.sequenceIOOption(); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestSome((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, list.length); - }); - - test('None', () { - var sideEffect = 0; - final list = [ - IOOption(() { - sideEffect += 1; - return some(1); - }), - IOOption(() { - sideEffect += 1; - return none<int>(); - }), - IOOption(() { - sideEffect += 1; - return some(3); - }), - IOOption(() { - sideEffect += 1; - return some(4); - }), - ]; - final traverse = list.sequenceIOOption(); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - }); - - group('FpdartSequenceIterableTaskOption', () { - group('sequenceTaskOption', () { - test('Some', () async { - var sideEffect = 0; - final list = [ - TaskOption(() async { - sideEffect += 1; - return some(1); - }), - TaskOption(() async { - sideEffect += 1; - return some(2); - }), - TaskOption(() async { - sideEffect += 1; - return some(3); - }), - TaskOption(() async { - sideEffect += 1; - return some(4); - }), - ]; - final traverse = list.sequenceTaskOption(); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, list.length); - }); - - test('None', () async { - var sideEffect = 0; - final list = [ - TaskOption(() async { - sideEffect += 1; - return some(1); - }), - TaskOption(() async { - sideEffect += 1; - return none<int>(); - }), - TaskOption(() async { - sideEffect += 1; - return some(3); - }), - TaskOption(() async { - sideEffect += 1; - return some(4); - }), - ]; - final traverse = list.sequenceTaskOption(); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - - group('sequenceTaskOptionSeq', () { - test('Some', () async { - var sideEffect = 0; - final list = [ - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 0; - return some(1); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return some(2); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return some(3); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return some(4); - }), - ]; - final traverse = list.sequenceTaskOptionSeq(); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, 3); - }); - - test('None', () async { - var sideEffect = 0; - final list = [ - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 0; - return some(1); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return none<int>(); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return some(3); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return some(4); - }), - ]; - final traverse = list.sequenceTaskOptionSeq(); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, 3); - }); - }); - }); - - group('FpdartSequenceIterableTaskEither', () { - group('sequenceTaskEither', () { - test('Right', () async { - var sideEffect = 0; - final list = [ - TaskEither(() async { - sideEffect += 1; - return right<String, int>(1); - }), - TaskEither(() async { - sideEffect += 1; - return right<String, int>(2); - }), - TaskEither(() async { - sideEffect += 1; - return right<String, int>(3); - }), - TaskEither(() async { - sideEffect += 1; - return right<String, int>(4); - }), - ]; - final traverse = list.sequenceTaskEither(); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, list.length); - }); - - test('Left', () async { - var sideEffect = 0; - final list = [ - TaskEither(() async { - sideEffect += 1; - return right<String, int>(1); - }), - TaskEither(() async { - sideEffect += 1; - return left<String, int>("Error"); - }), - TaskEither(() async { - sideEffect += 1; - return right<String, int>(3); - }), - TaskEither(() async { - sideEffect += 1; - return right<String, int>(4); - }), - ]; - final traverse = list.sequenceTaskEither(); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - - group('sequenceTaskEitherSeq', () { - test('Right', () async { - var sideEffect = 0; - final list = [ - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 0; - return right<String, int>(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return right<String, int>(2); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return right<String, int>(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return right<String, int>(4); - }), - ]; - final traverse = list.sequenceTaskEitherSeq(); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, 3); - }); - - test('Left', () async { - var sideEffect = 0; - final list = [ - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 0; - return right<String, int>(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return left<String, int>("Error"); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return right<String, int>(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return right<String, int>(4); - }), - ]; - final traverse = list.sequenceTaskEitherSeq(); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, 3); - }); - }); - }); - - group('FpdartSequenceIterableIOEither', () { - group('sequenceIOEither', () { - test('Right', () { - var sideEffect = 0; - final list = [ - IOEither(() { - sideEffect += 1; - return right<String, int>(1); - }), - IOEither(() { - sideEffect += 1; - return right<String, int>(2); - }), - IOEither(() { - sideEffect += 1; - return right<String, int>(3); - }), - IOEither(() { - sideEffect += 1; - return right<String, int>(4); - }), - ]; - final traverse = list.sequenceIOEither(); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestRight((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, list.length); - }); - - test('Left', () { - var sideEffect = 0; - final list = [ - IOEither(() { - sideEffect += 1; - return right<String, int>(1); - }), - IOEither(() { - sideEffect += 1; - return left<String, int>("Error"); - }), - IOEither(() { - sideEffect += 1; - return right<String, int>(3); - }), - IOEither(() { - sideEffect += 1; - return right<String, int>(4); - }), - ]; - final traverse = list.sequenceIOEither(); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/extension/list_extension_test.dart b/packages/fpdart/test/src/extension/list_extension_test.dart deleted file mode 100644 index 8e3139a..0000000 --- a/packages/fpdart/test/src/extension/list_extension_test.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import '../utils/utils.dart'; - -void main() { - group('FpdartOnList', () { - test('foldRight', () { - final list1 = [1, 2, 3]; - final ap = list1.foldRight(0.0, (t, b) => b - t); - expect(ap, 2); - }); - - test('foldRightWithIndex', () { - final list1 = [1, 2, 3]; - final ap = list1.foldRightWithIndex(0.0, (t, b, i) => b - t - i); - expect(ap, 1); - }); - - test('takeWhileRight', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.takeWhileRight((t) => t > 2); - expect(ap.length, 2); - expect(ap.elementAt(0), 4); - expect(ap.elementAt(1), 3); - }); - - test('dropWhileRight', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.dropWhileRight((t) => t > 2); - expect(ap.length, 2); - expect(ap.elementAt(0), 2); - expect(ap.elementAt(1), 1); - }); - }); -} diff --git a/packages/fpdart/test/src/extension/map_extension_test.dart b/packages/fpdart/test/src/extension/map_extension_test.dart deleted file mode 100644 index d9d8047..0000000 --- a/packages/fpdart/test/src/extension/map_extension_test.dart +++ /dev/null @@ -1,627 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import '../utils/utils.dart'; - -void main() { - group('FpdartOnMutableMap', () { - test('mapValue', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.mapValue((value) => '${value * 2}'), - {'a': '2', 'b': '4', 'c': '6', 'd': '8'}, - ); - }); - }); - - test('mapWithIndex', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.mapWithIndex((value, index) => '${value + index}'), - {'a': '1', 'b': '3', 'c': '5', 'd': '7'}, - ); - }); - }); - - test('filter', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.filter((t) => t > 2), - {'c': 3, 'd': 4}, - ); - }); - }); - - test('filterWithIndex', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.filterWithIndex((t, i) => t > 2 && i != 3), - {'c': 3}, - ); - }); - }); - - test('filterWithKey', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.filterWithKey((k, v) => v > 2 && k != 'd'), - {'c': 3}, - ); - }); - }); - - test('filterWithKeyAndIndex', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.filterWithKeyAndIndex((k, v, i) => v > 1 && i != 1 && k != 'd'), - {'c': 3}, - ); - }); - }); - - group('lookup', () { - test('Some', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value.lookup('b').matchTestSome((t) { - expect(t, 2); - }); - }); - }); - - test('None', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect(value.lookup('e'), isA<None>()); - }); - }); - }); - - group('lookupEq', () { - test('Some', () { - testImmutableMap({ - DateTime(2000, 1, 1): 1, - DateTime(2001, 1, 1): 2, - }, (value) { - value - .lookupEq(Eq.dateEqYear, DateTime(2000, 10, 10)) - .matchTestSome((t) { - expect(t, 1); - }); - }); - }); - - test('None', () { - testImmutableMap({ - DateTime(2000, 1, 1): 1, - DateTime(2001, 1, 1): 2, - }, (value) { - expect( - value.lookupEq(Eq.dateEqYear, DateTime(2002, 1, 1)), isA<None>()); - }); - }); - }); - - group('lookupWithKeyEq', () { - test('Some', () { - testImmutableMap({ - DateTime(2000, 1, 1): 1, - DateTime(2001, 1, 1): 2, - }, (value) { - value - .lookupWithKeyEq( - Eq.dateEqYear, - DateTime(2000, 10, 10), - ) - .matchTestSome((t) { - expect(t, (DateTime(2000, 1, 1), 1)); - }); - }); - }); - - test('None', () { - testImmutableMap({ - DateTime(2000, 1, 1): 1, - DateTime(2001, 1, 1): 2, - }, (value) { - expect( - value.lookupWithKeyEq(Eq.dateEqYear, DateTime(2002, 1, 1)), - isA<None>(), - ); - }); - }); - }); - - group('lookupWithKey', () { - test('Some', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value.lookupWithKey('b').matchTestSome((t) { - expect(t.$1, 'b'); - expect(t.$2, 2); - }); - }); - }); - - test('None', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect(value.lookupWithKey('e'), isA<None>()); - }); - }); - }); - - group('lookupKeyEq', () { - test('Some', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value.lookupKeyEq(Eq.eqString, 'b').matchTestSome((t) { - expect(t, 'b'); - }); - }); - }); - - test('None', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect(value.lookupKeyEq(Eq.eqString, 'e'), isA<None>()); - }); - }); - }); - - group('extract', () { - test('valid', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value.extract<int>('b').matchTestSome((t) { - expect(t, 2); - }); - }); - }); - - test('wrong type', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect(value.extract<String>('b'), isA<None>()); - }); - }); - }); - - group('extractMap', () { - test('no map', () { - testImmutableMap({'a': 1}, (value) { - expect(value.extractMap('a'), isA<None>()); - }); - }); - - test('one level', () { - testImmutableMap({ - 'a': {'b': 2} - }, (value) { - expect(value.extractMap('a').toNullable(), equals({'b': 2})); - }); - }); - - test('two levels', () { - testImmutableMap({ - 'a': { - 'b': {'c': 3} - } - }, (value) { - expect(value.extractMap('a').extractMap('b').toNullable(), - equals({'c': 3})); - }); - }); - - test('two levels with extract', () { - testImmutableMap({ - 'a': { - 'b': {'c': 3} - } - }, (value) { - value - .extractMap('a') - .extractMap('b') - .extract<int>('c') - .matchTestSome((t) { - expect(t, 3); - }); - }); - }); - }); - - group('modifyAt', () { - test('Some', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value - .modifyAt( - Eq.instance((a1, a2) => a1 == a2), - (v) => v + 2, - 'b', - ) - .matchTestSome( - (t) => t.lookup('b').matchTestSome((t) { - expect(t, 4); - }), - ); - }); - }); - - test('None', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.modifyAt( - Eq.instance((a1, a2) => a1 == a2), - (v) => v + 2, - 'e', - ), - isA<None>(), - ); - }); - }); - }); - - group('modifyAtIfPresent', () { - test('found', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value - .modifyAtIfPresent( - Eq.instance((a1, a2) => a1 == a2), - (v) => v + 2, - 'b', - ) - .lookup('b') - .matchTestSome((t) { - expect(t, 4); - }); - }); - }); - - test('not found', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value - .modifyAtIfPresent( - Eq.instance((a1, a2) => a1 == a2), - (v) => v + 2, - 'e', - ) - .lookup('b') - .matchTestSome((t) { - expect(t, 2); - }); - }); - }); - }); - - group('updateAt', () { - test('Some', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value.updateAt(Eq.eqString, 'b', 10).matchTestSome( - (t) => t.lookup('b').matchTestSome((t) { - expect(t, 10); - }), - ); - }); - }); - - test('None', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect(value.updateAt(Eq.eqString, 'e', 10), isA<None>()); - }); - }); - }); - - group('updateAtIfPresent', () { - test('found', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value - .updateAtIfPresent(Eq.instance((a1, a2) => a1 == a2), 'b', 10) - .lookup('b') - .matchTestSome((t) { - expect(t, 10); - }); - }); - }); - - test('not found', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value - .updateAtIfPresent(Eq.instance((a1, a2) => a1 == a2), 'e', 10) - .lookup('b') - .matchTestSome((t) { - expect(t, 2); - }); - }); - }); - }); - - test('deleteAt', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect(value.lookup('b'), isA<Some>()); - - final result = value.deleteAt(Eq.instance((a1, a2) => a1 == a2), 'b'); - expect(value.lookup('b'), isA<Some>()); - expect(result.lookup('b'), isA<None>()); - }); - }); - - group('upsertAt', () { - test('insert', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect(value.lookup('e'), isA<None>()); - - final result = - value.upsertAt(Eq.instance((a1, a2) => a1 == a2), 'e', 10); - expect(value.lookup('e'), isA<None>()); - - result.lookup('e').matchTestSome((t) { - expect(t, 10); - }); - }); - }); - - test('update', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value.lookupEq(Eq.eqString, 'b').matchTestSome((t) { - expect(t, 2); - }); - - final result = value.upsertAt(Eq.eqString, 'b', 10); - value.lookupEq(Eq.eqString, 'b').matchTestSome((t) { - expect(t, 2); - }); - result.lookupEq(Eq.eqString, 'b').matchTestSome((t) { - expect(t, 10); - }); - }); - }); - - test('modify by eq date year', () { - testImmutableMap(<DateTime, int>{}, (value) { - final d1 = DateTime(2001, 1, 1); - final d2 = DateTime(2001, 1, 2); - - final result = value - .upsertAt( - Eq.dateEqYear, - d1, - 1, - ) - .upsertAt( - Eq.dateEqYear, - d2, - 2, - ); - - result.lookupEq(Eq.dateEqYear, d1).matchTestSome((t) { - expect(t, 2); - }); - result.lookupEq(Eq.dateEqYear, d2).matchTestSome((t) { - expect(t, 2); - }); - }); - }); - }); - - group('pop', () { - test('Some', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - final result = value.pop(Eq.instance((a1, a2) => a1 == a2), 'b'); - expect(value.lookup('b'), isA<Some>()); - - result.matchTestSome((t) { - expect(t.$1, 2); - expect(t.$2.lookup('b'), isA<None>()); - }); - }); - }); - - test('None', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.pop(Eq.instance((a1, a2) => a1 == a2), 'e'), - isA<None>(), - ); - }); - }); - }); - - test('foldLeft', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldLeft<String>(Order.allEqual(), '', (acc, a) => '$acc$a'), - '1234', - ); - }); - }); - - test('foldLeftWithIndex', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldLeftWithIndex<String>( - Order.allEqual(), '', (acc, a, i) => '$acc$a$i'), - '10213243', - ); - }); - }); - - test('foldLeftWithKey', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldLeftWithKey<String>( - Order.allEqual(), '', (acc, k, v) => '$acc$k$v'), - 'a1b2c3d4', - ); - }); - }); - - test('foldLeftWithKeyAndIndex', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldLeftWithKeyAndIndex<String>( - Order.allEqual(), '', (acc, k, v, i) => '$acc$k$v$i'), - 'a10b21c32d43', - ); - }); - }); - - test('foldRight', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldRight<String>(Order.allEqual(), '', (a, acc) => '$acc$a'), - '4321', - ); - }); - }); - - test('foldRightWithIndex', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldRightWithIndex<String>( - Order.allEqual(), '', (a, acc, i) => '$acc$a$i'), - '40312213', - ); - }); - }); - - test('foldRightWithKey', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldRightWithKey<String>( - Order.allEqual(), '', (k, v, acc) => '$acc$k$v'), - 'd4c3b2a1', - ); - }); - }); - - test('foldRightWithKeyAndIndex', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldRightWithKeyAndIndex<String>( - Order.allEqual(), '', (k, v, acc, i) => '$acc$k$v$i'), - 'd40c31b22a13', - ); - }); - }); - - test('size', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect(value.size, 4); - }); - }); - - test('toSortedList', () { - testImmutableMap({'c': 3, 'd': 4, 'a': 1, 'b': 2}, (value) { - final result = - value.toSortedList(Order.from((a1, a2) => a1.compareTo(a2))); - expect(result.elementAt(0).value, 1); - expect(result.elementAt(1).value, 2); - expect(result.elementAt(2).value, 3); - expect(result.elementAt(3).value, 4); - expect(result.elementAt(0).key, 'a'); - expect(result.elementAt(1).key, 'b'); - expect(result.elementAt(2).key, 'c'); - expect(result.elementAt(3).key, 'd'); - }); - }); - - test('union', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value1) { - testImmutableMap({'c': 20, 'e': 10}, (value2) { - final ap = value1.union( - Eq.instance((a1, a2) => a1 == a2), - (x, y) => x + y, - value2, - ); - - expect(ap['a'], 1); - expect(ap['b'], 2); - expect(ap['c'], 23); - expect(ap['d'], 4); - expect(ap['e'], 10); - }); - }); - }); - - test('intersection', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value1) { - testImmutableMap({'c': 20, 'e': 10}, (value2) { - final ap = value1.intersection( - Eq.instance((a1, a2) => a1 == a2), - (x, y) => x + y, - value2, - ); - - expect(ap['a'], null); - expect(ap['b'], null); - expect(ap['c'], 23); - expect(ap['d'], null); - expect(ap['e'], null); - }); - }); - }); - - group('isSubmap', () { - test('true', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value1) { - testImmutableMap({'a': 1, 'c': 3}, (value2) { - final result = value2.isSubmap( - Eq.eqString, - Eq.eqInt, - value1, - ); - - expect(result, true); - }); - }); - }); - - test('false (value)', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value1) { - testImmutableMap({'a': 1, 'c': 2}, (value2) { - final result = value2.isSubmap( - Eq.eqString, - Eq.eqInt, - value1, - ); - - expect(result, false); - }); - }); - }); - - test('false (key)', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value1) { - testImmutableMap({'a': 1, 'd': 3}, (value2) { - final result = value2.isSubmap( - Eq.eqString, - Eq.eqInt, - value1, - ); - - expect(result, false); - }); - }); - }); - }); - - test('collect', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - final result = value.collect<String>( - Order.from( - (a1, a2) => a1.compareTo(a2), - ), - (k, v) => '$k$v', - ); - - expect(result.elementAt(0), 'a1'); - expect(result.elementAt(1), 'b2'); - expect(result.elementAt(2), 'c3'); - expect(result.elementAt(3), 'd4'); - }); - }); - - test('difference', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value1) { - testImmutableMap({'a': 1, 'c': 3}, (value2) { - final result = value1.difference(Eq.eqString, value2); - expect(result['a'], null); - expect(result['b'], 2); - expect(result['c'], null); - expect(result['d'], 4); - }); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/extension/option_extension_test.dart b/packages/fpdart/test/src/extension/option_extension_test.dart deleted file mode 100644 index 1734493..0000000 --- a/packages/fpdart/test/src/extension/option_extension_test.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import '../utils/utils.dart'; - -void main() { - group('FpdartOnOption', () { - group('alt', () { - test('Some', () { - final option = Option.of(10); - final value = option.alt(() => Option.of(0)); - value.matchTestSome((some) => expect(some, 10)); - }); - - test('None', () { - final option = Option<int>.none(); - final value = option.alt(() => Option.of(0)); - value.matchTestSome((some) => expect(some, 0)); - }); - }); - - group('getOrElse', () { - test('Some', () { - final option = Option.of(10); - final value = option.getOrElse(() => 0); - expect(value, 10); - }); - - test('None', () { - final option = Option<int>.none(); - final value = option.getOrElse(() => 0); - expect(value, 0); - }); - }); - - test('elem', () { - final m1 = Option.of(10); - final m2 = Option<int>.none(); - final eq = Eq.instance<int>((a1, a2) => a1 == a2); - expect(m1.elem(10, eq), true); - expect(m1.elem(9, eq), false); - expect(m2.elem(10, eq), false); - }); - }); -} diff --git a/packages/fpdart/test/src/extension/predicate_extension_test.dart b/packages/fpdart/test/src/extension/predicate_extension_test.dart deleted file mode 100644 index 5d1c79d..0000000 --- a/packages/fpdart/test/src/extension/predicate_extension_test.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:glados/glados.dart'; - -void main() { - group('Predicate extension', () { - group('FpdartOnPredicate', () { - Glados<bool>(any.bool).test('negate', (boolValue) { - bool fun() => boolValue; - expect(fun(), !fun.negate); - }); - - test('and', () { - bool funTrue() => true; - bool funFalse() => false; - expect(funTrue.and(funTrue)(), true); - expect(funTrue.and(funFalse)(), false); - expect(funFalse.and(funTrue)(), false); - expect(funFalse.and(funFalse)(), false); - }); - - test('or', () { - bool funTrue() => true; - bool funFalse() => false; - expect(funTrue.or(funTrue)(), true); - expect(funTrue.or(funFalse)(), true); - expect(funFalse.or(funTrue)(), true); - expect(funFalse.or(funFalse)(), false); - }); - - test('xor', () { - bool funTrue() => true; - bool funFalse() => false; - expect(funTrue.xor(funTrue)(), false); - expect(funTrue.xor(funFalse)(), true); - expect(funFalse.xor(funTrue)(), true); - expect(funFalse.xor(funFalse)(), false); - }); - }); - - group('FpdartOnPredicate1', () { - Glados<int>(any.int).test('negate', (intValue) { - bool fun(int n) => n % 2 == 0; - expect(fun(intValue), !fun.negate(intValue)); - }); - - test('and', () { - bool fun2(int n) => n % 2 == 0; - bool fun3(int n) => n % 3 == 0; - expect(fun2.and(fun2)(4), true); - expect(fun2.and(fun3)(4), false); - expect(fun2.and(fun3)(6), true); - expect(fun3.and(fun2)(4), false); - expect(fun3.and(fun3)(3), true); - }); - - test('or', () { - bool fun2(int n) => n % 2 == 0; - bool fun3(int n) => n % 3 == 0; - expect(fun2.or(fun2)(4), true); - expect(fun2.or(fun3)(4), true); - expect(fun2.or(fun3)(6), true); - expect(fun3.or(fun2)(4), true); - expect(fun3.or(fun2)(7), false); - expect(fun3.or(fun3)(7), false); - }); - - test('xor', () { - bool fun2(int n) => n % 2 == 0; - bool fun3(int n) => n % 3 == 0; - expect(fun2.xor(fun2)(4), false); - expect(fun2.xor(fun3)(4), true); - expect(fun2.xor(fun3)(6), false); - expect(fun3.xor(fun2)(4), true); - expect(fun3.xor(fun3)(3), false); - expect(fun3.xor(fun2)(7), false); - }); - - test('contramap', () { - bool even(int n) => n % 2 == 0; - final evenLength = even.contramap<String>((a) => a.length); - expect(evenLength("abc"), false); - expect(evenLength("abcd"), true); - expect(evenLength("a"), false); - expect(evenLength("ab"), true); - expect(evenLength("abcde"), false); - expect(evenLength("abcdef"), true); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/extension/string_extension_test.dart b/packages/fpdart/test/src/extension/string_extension_test.dart deleted file mode 100644 index af54be4..0000000 --- a/packages/fpdart/test/src/extension/string_extension_test.dart +++ /dev/null @@ -1,858 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -import '../utils/match_utils.dart'; - -void main() { - group('FpdartOnString', () { - group('toNumOption', () { - group('Some', () { - test('"10"', () { - final result = "10".toNumOption; - result.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('"10.0"', () { - final result = "10.0".toNumOption; - result.matchTestSome((t) { - expect(t, 10.0); - }); - }); - - test('"10.5"', () { - final result = "10.5".toNumOption; - result.matchTestSome((t) { - expect(t, 10.5); - }); - }); - - test('"10.512"', () { - final result = "10.512".toNumOption; - result.matchTestSome((t) { - expect(t, 10.512); - }); - }); - - test('" 10 "', () { - final result = " 10 ".toNumOption; - result.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('"-10"', () { - final result = "-10".toNumOption; - result.matchTestSome((t) { - expect(t, -10); - }); - }); - - test('" 3.14 \xA0"', () { - final result = " 3.14 \xA0".toNumOption; - result.matchTestSome((t) { - expect(t, 3.14); - }); - }); - - test('"0."', () { - final result = "0.".toNumOption; - result.matchTestSome((t) { - expect(t, 0); - }); - }); - - test('".0"', () { - final result = ".0".toNumOption; - result.matchTestSome((t) { - expect(t, 0); - }); - }); - - test('"-1.e3"', () { - final result = "-1.e3".toNumOption; - result.matchTestSome((t) { - expect(t, -1000); - }); - }); - - test('"1234E+7"', () { - final result = "1234E+7".toNumOption; - result.matchTestSome((t) { - expect(t, 12340000000); - }); - }); - - test('"0xFF"', () { - final result = "0xFF".toNumOption; - result.matchTestSome((t) { - expect(t, 255); - }); - }); - }); - - group('None', () { - test('"- 10"', () { - final result = "- 10".toNumOption; - expect(result, isA<None>()); - }); - - test('"10,0"', () { - final result = "10,0".toNumOption; - expect(result, isA<None>()); - }); - - test('"10a"', () { - final result = "10a".toNumOption; - expect(result, isA<None>()); - }); - - test('"none"', () { - final result = "none".toNumOption; - expect(result, isA<None>()); - }); - - test('"10.512a"', () { - final result = "10.512a".toNumOption; - expect(result, isA<None>()); - }); - - test('"1f"', () { - final result = "1f".toNumOption; - expect(result, isA<None>()); - }); - }); - }); - - group('toIntOption', () { - group('Some', () { - test('"10"', () { - final result = "10".toIntOption; - result.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('"-10"', () { - final result = "-10".toIntOption; - result.matchTestSome((t) { - expect(t, -10); - }); - }); - - test('" 10 "', () { - final result = " 10 ".toIntOption; - result.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('"0xFF"', () { - final result = "0xFF".toIntOption; - result.matchTestSome((t) { - expect(t, 255); - }); - }); - }); - - group('None', () { - test('"- 10"', () { - final result = "- 10".toIntOption; - expect(result, isA<None>()); - }); - - test('"-1.e3"', () { - final result = "-1.e3".toIntOption; - expect(result, isA<None>()); - }); - - test('"1234E+7"', () { - final result = "1234E+7".toIntOption; - expect(result, isA<None>()); - }); - - test('"0."', () { - final result = "0.".toIntOption; - expect(result, isA<None>()); - }); - - test('"10.5"', () { - final result = "10.5".toIntOption; - expect(result, isA<None>()); - }); - - test('"10.0"', () { - final result = "10.0".toIntOption; - expect(result, isA<None>()); - }); - - test('"10.512"', () { - final result = "10.512".toIntOption; - expect(result, isA<None>()); - }); - - test('" 3.14 \xA0"', () { - final result = " 3.14 \xA0".toIntOption; - expect(result, isA<None>()); - }); - - test('".0"', () { - final result = ".0".toIntOption; - expect(result, isA<None>()); - }); - - test('"10,0"', () { - final result = "10,0".toIntOption; - expect(result, isA<None>()); - }); - - test('"10a"', () { - final result = "10a".toIntOption; - expect(result, isA<None>()); - }); - - test('"none"', () { - final result = "none".toIntOption; - expect(result, isA<None>()); - }); - - test('"10.512a"', () { - final result = "10.512a".toIntOption; - expect(result, isA<None>()); - }); - - test('"1f"', () { - final result = "1f".toIntOption; - expect(result, isA<None>()); - }); - }); - }); - - group('toDoubleOption', () { - group('Some', () { - test('"10"', () { - final result = "10".toDoubleOption; - result.matchTestSome((t) { - expect(t, 10.0); - }); - }); - - test('"10.0"', () { - final result = "10.0".toDoubleOption; - result.matchTestSome((t) { - expect(t, 10.0); - }); - }); - - test('"10.5"', () { - final result = "10.5".toDoubleOption; - result.matchTestSome((t) { - expect(t, 10.5); - }); - }); - - test('"10.512"', () { - final result = "10.512".toDoubleOption; - result.matchTestSome((t) { - expect(t, 10.512); - }); - }); - - test('" 10 "', () { - final result = " 10 ".toDoubleOption; - result.matchTestSome((t) { - expect(t, 10.0); - }); - }); - - test('"-10"', () { - final result = "-10".toDoubleOption; - result.matchTestSome((t) { - expect(t, -10.0); - }); - }); - - test('" 3.14 \xA0"', () { - final result = " 3.14 \xA0".toDoubleOption; - result.matchTestSome((t) { - expect(t, 3.14); - }); - }); - - test('"0."', () { - final result = "0.".toDoubleOption; - result.matchTestSome((t) { - expect(t, 0.0); - }); - }); - - test('".0"', () { - final result = ".0".toDoubleOption; - result.matchTestSome((t) { - expect(t, 0.0); - }); - }); - - test('"-1.e3"', () { - final result = "-1.e3".toDoubleOption; - result.matchTestSome((t) { - expect(t, -1000.0); - }); - }); - - test('"1234E+7"', () { - final result = "1234E+7".toDoubleOption; - result.matchTestSome((t) { - expect(t, 12340000000.0); - }); - }); - }); - - group('None', () { - test('"0xFF"', () { - final result = "0xFF".toDoubleOption; - expect(result, isA<None>()); - }); - - test('"- 10"', () { - final result = "- 10".toDoubleOption; - expect(result, isA<None>()); - }); - - test('"10,0"', () { - final result = "10,0".toDoubleOption; - expect(result, isA<None>()); - }); - - test('"10a"', () { - final result = "10a".toDoubleOption; - expect(result, isA<None>()); - }); - - test('"none"', () { - final result = "none".toDoubleOption; - expect(result, isA<None>()); - }); - - test('"10.512a"', () { - final result = "10.512a".toDoubleOption; - expect(result, isA<None>()); - }); - - test('"1f"', () { - final result = "1f".toDoubleOption; - expect(result, isA<None>()); - }); - }); - }); - - group('toBoolOption', () { - group('Some', () { - test('"true"', () { - final result = "true".toBoolOption; - result.matchTestSome((t) { - expect(t, true); - }); - }); - - test('"false"', () { - final result = "false".toBoolOption; - result.matchTestSome((t) { - expect(t, false); - }); - }); - }); - - group('None', () { - test('"TRUE"', () { - final result = "TRUE".toBoolOption; - expect(result, isA<None>()); - }); - - test('"FALSE"', () { - final result = "FALSE".toBoolOption; - expect(result, isA<None>()); - }); - - test('"NO"', () { - final result = "NO".toBoolOption; - expect(result, isA<None>()); - }); - - test('"YES"', () { - final result = "YES".toBoolOption; - expect(result, isA<None>()); - }); - - test('"0"', () { - final result = "0".toBoolOption; - expect(result, isA<None>()); - }); - - test('"1"', () { - final result = "1".toBoolOption; - expect(result, isA<None>()); - }); - }); - }); - - group('toNumEither', () { - group('Right', () { - test('"10"', () { - final result = "10".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10); - }); - }); - - test('"10.0"', () { - final result = "10.0".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10.0); - }); - }); - - test('"10.5"', () { - final result = "10.5".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10.5); - }); - }); - - test('"10.512"', () { - final result = "10.512".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10.512); - }); - }); - - test('" 10 "', () { - final result = " 10 ".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10); - }); - }); - - test('"-10"', () { - final result = "-10".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, -10); - }); - }); - - test('" 3.14 \xA0"', () { - final result = " 3.14 \xA0".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, 3.14); - }); - }); - - test('"0."', () { - final result = "0.".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, 0); - }); - }); - - test('".0"', () { - final result = ".0".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, 0); - }); - }); - - test('"-1.e3"', () { - final result = "-1.e3".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, -1000); - }); - }); - - test('"1234E+7"', () { - final result = "1234E+7".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, 12340000000); - }); - }); - - test('"0xFF"', () { - final result = "0xFF".toNumEither(() => "left"); - result.matchTestRight((t) { - expect(t, 255); - }); - }); - }); - - group('Left', () { - test('"- 10"', () { - final result = "- 10".toNumEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10,0"', () { - final result = "10,0".toNumEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10a"', () { - final result = "10a".toNumEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"none"', () { - final result = "none".toNumEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10.512a"', () { - final result = "10.512a".toNumEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"1f"', () { - final result = "1f".toNumEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - }); - }); - - group('toIntEither', () { - group('Some', () { - test('"10"', () { - final result = "10".toIntEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10); - }); - }); - - test('"-10"', () { - final result = "-10".toIntEither(() => "left"); - result.matchTestRight((t) { - expect(t, -10); - }); - }); - - test('" 10 "', () { - final result = " 10 ".toIntEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10); - }); - }); - - test('"0xFF"', () { - final result = "0xFF".toIntEither(() => "left"); - result.matchTestRight((t) { - expect(t, 255); - }); - }); - }); - - group('None', () { - test('"- 10"', () { - final result = "- 10".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"-1.e3"', () { - final result = "-1.e3".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"1234E+7"', () { - final result = "1234E+7".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"0."', () { - final result = "0.".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10.5"', () { - final result = "10.5".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10.0"', () { - final result = "10.0".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10.512"', () { - final result = "10.512".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('" 3.14 \xA0"', () { - final result = " 3.14 \xA0".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('".0"', () { - final result = ".0".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10,0"', () { - final result = "10,0".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10a"', () { - final result = "10a".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"none"', () { - final result = "none".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10.512a"', () { - final result = "10.512a".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"1f"', () { - final result = "1f".toIntEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - }); - }); - - group('toDoubleEither', () { - group('Some', () { - test('"10"', () { - final result = "10".toDoubleEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10.0); - }); - }); - - test('"10.0"', () { - final result = "10.0".toDoubleEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10.0); - }); - }); - - test('"10.5"', () { - final result = "10.5".toDoubleEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10.5); - }); - }); - - test('"10.512"', () { - final result = "10.512".toDoubleEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10.512); - }); - }); - - test('" 10 "', () { - final result = " 10 ".toDoubleEither(() => "left"); - result.matchTestRight((t) { - expect(t, 10.0); - }); - }); - - test('"-10"', () { - final result = "-10".toDoubleEither(() => "left"); - result.matchTestRight((t) { - expect(t, -10.0); - }); - }); - - test('" 3.14 \xA0"', () { - final result = " 3.14 \xA0".toDoubleEither(() => "left"); - result.matchTestRight((t) { - expect(t, 3.14); - }); - }); - - test('"0."', () { - final result = "0.".toDoubleEither(() => "left"); - result.matchTestRight((t) { - expect(t, 0.0); - }); - }); - - test('".0"', () { - final result = ".0".toDoubleEither(() => "left"); - result.matchTestRight((t) { - expect(t, 0.0); - }); - }); - - test('"-1.e3"', () { - final result = "-1.e3".toDoubleEither(() => "left"); - result.matchTestRight((t) { - expect(t, -1000.0); - }); - }); - - test('"1234E+7"', () { - final result = "1234E+7".toDoubleEither(() => "left"); - result.matchTestRight((t) { - expect(t, 12340000000.0); - }); - }); - }); - - group('None', () { - test('"0xFF"', () { - final result = "0xFF".toDoubleEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"- 10"', () { - final result = "- 10".toDoubleEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10,0"', () { - final result = "10,0".toDoubleEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10a"', () { - final result = "10a".toDoubleEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"none"', () { - final result = "none".toDoubleEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"10.512a"', () { - final result = "10.512a".toDoubleEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"1f"', () { - final result = "1f".toDoubleEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - }); - }); - - group('toBoolEither', () { - group('Some', () { - test('"true"', () { - final result = "true".toBoolEither(() => "left"); - result.matchTestRight((t) { - expect(t, true); - }); - }); - - test('"false"', () { - final result = "false".toBoolEither(() => "left"); - result.matchTestRight((t) { - expect(t, false); - }); - }); - }); - - group('None', () { - test('"TRUE"', () { - final result = "TRUE".toBoolEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"FALSE"', () { - final result = "FALSE".toBoolEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"NO"', () { - final result = "NO".toBoolEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"YES"', () { - final result = "YES".toBoolEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"0"', () { - final result = "0".toBoolEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - - test('"1"', () { - final result = "1".toBoolEither(() => "left"); - result.matchTestLeft((t) { - expect(t, "left"); - }); - }); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/function_test.dart b/packages/fpdart/test/src/function_test.dart deleted file mode 100644 index 8093408..0000000 --- a/packages/fpdart/test/src/function_test.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:glados/glados.dart'; - -void main() { - group('function', () { - group('[Property-based testing]', () { - Glados(any.letterOrDigits).test('identity', (stringValue) { - expect(identity(stringValue), stringValue); - }); - - Glados(any.letterOrDigits).test('constF', (stringValue) { - final fun = constF(10); - expect(fun(stringValue), 10); - }); - - Glados(any.letterOrDigits).test('identityFuture', (stringValue) async { - final id = await identityFuture(stringValue); - expect(id, stringValue); - }); - - Glados(any.letterOrDigits).test('toNumOption (same as extension)', - (stringValue) { - expect(stringValue.toNumOption, toNumOption(stringValue)); - }); - - Glados(any.letterOrDigits).test('toIntOption (same as extension)', - (stringValue) { - expect(stringValue.toIntOption, toIntOption(stringValue)); - }); - - Glados(any.letterOrDigits).test('toDoubleOption (same as extension)', - (stringValue) { - expect(stringValue.toDoubleOption, toDoubleOption(stringValue)); - }); - - Glados(any.letterOrDigits).test('toBoolOption (same as extension)', - (stringValue) { - expect(stringValue.toBoolOption, toBoolOption(stringValue)); - }); - - Glados(any.letterOrDigits).test('toNumEither (same as extension)', - (stringValue) { - expect(stringValue.toNumEither(() => "left"), - toNumEither(() => "left")(stringValue)); - }); - - Glados(any.letterOrDigits).test('toIntEither (same as extension)', - (stringValue) { - expect(stringValue.toIntEither(() => "left"), - toIntEither(() => "left")(stringValue)); - }); - - Glados(any.letterOrDigits).test('toDoubleEither (same as extension)', - (stringValue) { - expect(stringValue.toDoubleEither(() => "left"), - toDoubleEither(() => "left")(stringValue)); - }); - - Glados(any.letterOrDigits).test('toBoolEither (same as extension)', - (stringValue) { - expect(stringValue.toBoolEither(() => "left"), - toBoolEither(stringValue, () => "left")); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/group_test.dart b/packages/fpdart/test/src/group_test.dart deleted file mode 100644 index a4ddfaf..0000000 --- a/packages/fpdart/test/src/group_test.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('Group', () { - group('is a', () { - final instance = Group.instance<int>(0, (a1, a2) => a1 + a2, (a) => -a); - - test('Semigroup', () { - expect(instance, isA<Semigroup>()); - }); - - test('Monoid', () { - expect(instance, isA<Monoid>()); - }); - }); - - test('inverse', () { - final instance = Group.instance<int>(0, (a1, a2) => a1 + a2, (a) => -a); - expect(instance.inverse(1), -1); - expect(instance.combine(1, instance.inverse(1)), instance.empty); - expect(instance.combine(instance.inverse(1), 1), instance.empty); - }); - - test('remove', () { - final instance = Group.instance<int>(0, (a1, a2) => a1 + a2, (a) => -a); - expect(instance.remove(1, 2), -1); - expect(instance.remove(2, 1), 1); - expect(instance.remove(1, 1), instance.empty); - expect(instance.remove(1, 2), instance.combine(1, instance.inverse(2))); - }); - - test('combineN', () { - final instance = Group.instance<int>(0, (a1, a2) => a1 + a2, (a) => -a); - expect(instance.combineN(1, 2), 2); - expect(instance.combineN(1, 1), 1); - expect(instance.combineN(1, 0), instance.empty); - expect(instance.combineN(1, -1), -1); - expect(instance.combineN(1, -2), -2); - - // Stack Overflow! - // expect(instance.combineN(2, 9223372036854775807), 2); - }); - }); -} diff --git a/packages/fpdart/test/src/hash_test.dart b/packages/fpdart/test/src/hash_test.dart deleted file mode 100644 index 89de271..0000000 --- a/packages/fpdart/test/src/hash_test.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('Hash', () { - group('is a', () { - final instance = Hash.fromUniversalHashCode<int>(); - - test('Eq', () { - expect(instance, isA<Eq>()); - }); - }); - - test('.fromUniversalHashCode', () { - const source1 = 1; - const source2 = 1; - final instance = Hash.fromUniversalHashCode<int>(); - expect(instance.hash(source1), source1.hashCode); - expect(instance.eqv(source1, source2), true); - expect(instance.hash(source1), instance.hash(source2)); - }); - }); -} diff --git a/packages/fpdart/test/src/io_either_test.dart b/packages/fpdart/test/src/io_either_test.dart deleted file mode 100644 index d553977..0000000 --- a/packages/fpdart/test/src/io_either_test.dart +++ /dev/null @@ -1,774 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import './utils/utils.dart'; - -void main() { - group('IOEither', () { - group('tryCatch', () { - test('Success', () { - final task = - IOEither<String, int>.tryCatch(() => 10, (_, __) => 'error'); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Failure', () { - final task = IOEither<String, int>.tryCatch(() { - throw UnimplementedError(); - }, (_, __) => 'error'); - final r = task.run(); - r.match((l) => expect(l, 'error'), (_) { - fail('should be left'); - }); - }); - - test('throws Exception', () { - final task = IOEither<String, int>.tryCatch(() { - throw UnimplementedError(); - }, (error, _) { - expect(error, isA<UnimplementedError>()); - return 'error'; - }); - final r = task.run(); - r.match((l) => expect(l, 'error'), (_) { - fail('should be left'); - }); - }); - }); - - group('flatMap', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = - task.flatMap((r) => IOEither<String, int>(() => Either.of(r + 10))); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 20)); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = - task.flatMap((r) => IOEither<String, int>(() => Either.of(r + 10))); - final r = ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('flatMapTask', () { - test('Right to Right', () async { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = task.flatMapTask((r) => TaskEither.of(r + 1)); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 11)); - }); - - test('Left to Right', () async { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = task.flatMapTask((r) => TaskEither.of(r + 1)); - final r = await ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - - test('Right to Left', () async { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = - task.flatMapTask((r) => TaskEither<String, int>.left('none')); - final r = await ap.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('Left to Left', () async { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = - task.flatMapTask((r) => TaskEither<String, int>.left('none')); - final r = await ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('toTaskEither', () { - test('Some', () async { - final task = IOEither(() => Either.of(10)); - final convert = task.toTaskEither(); - final r = await convert.run(); - r.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('None', () async { - final task = IOEither(() => Either.left('None')); - final convert = task.toTaskEither(); - final r = await convert.run(); - r.matchTestLeft((l) { - expect(l, 'None'); - }); - }); - }); - - group('ap', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = task.ap<double>(IOEither(() => Either.of((int c) => c / 2))); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 5.0)); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = task.ap<double>(IOEither(() => Either.of((int c) => c / 2))); - final r = ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('map', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = task.map((r) => r / 2); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 5.0)); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = task.map((r) => r / 2); - final r = ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('mapLeft', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = task.mapLeft((l) => l.length); - final r = ap.run(); - r.matchTestRight((r) => expect(r, 10)); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = task.mapLeft((l) => l.length); - final r = ap.run(); - r.matchTestLeft((l) => expect(l, 3)); - }); - }); - - group('bimap', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = task.bimap((l) => l.length, (r) => r / 2); - final r = ap.run(); - r.matchTestRight((r) => expect(r, 5.0)); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = task.bimap((l) => l.length, (r) => r / 2); - final r = ap.run(); - r.matchTestLeft((l) => expect(l, 3)); - }); - }); - - group('map2', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = task.map2<int, double>( - IOEither<String, int>(() => Either.of(2)), (b, c) => b / c); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 5.0)); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = task.map2<int, double>( - IOEither<String, int>(() => Either.of(2)), (b, c) => b / c); - final r = ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('map3', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = task.map3<int, int, double>( - IOEither<String, int>(() => Either.of(2)), - IOEither<String, int>(() => Either.of(5)), - (b, c, d) => b * c / d); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 4.0)); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = task.map3<int, int, double>( - IOEither<String, int>(() => Either.of(2)), - IOEither<String, int>(() => Either.of(5)), - (b, c, d) => b * c / d); - final r = ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('andThen', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = - task.andThen(() => IOEither<String, double>(() => Either.of(12.5))); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 12.5)); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = - task.andThen(() => IOEither<String, double>(() => Either.of(12.5))); - final r = ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('call', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = task(IOEither<String, double>(() => Either.of(12.5))); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 12.5)); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = task(IOEither<String, double>(() => Either.of(12.5))); - final r = ap.run(); - r.match((r) { - expect(r, 'abc'); - }, (_) { - fail('should be left'); - }); - }); - }); - - group('filterOrElse', () { - test('Right (true)', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = task.filterOrElse((r) => r > 5, (r) => 'abc'); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Right (false)', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ap = task.filterOrElse((r) => r < 5, (r) => 'none'); - final r = ap.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = task.filterOrElse((r) => r > 5, (r) => 'none'); - final r = ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - test('pure', () { - final task = IOEither<String, int>(() => Either.left('abc')); - final ap = task.pure('abc'); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 'abc')); - }); - - test('run', () { - final task = IOEither<String, int>(() => Either.of(10)); - final func = task.run(); - expect(func, isA<Either<String, int>>()); - final r = func; - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - group('fromEither', () { - test('Right', () { - final task = IOEither<String, int>.fromEither(Either.of(10)); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final task = IOEither<String, int>.fromEither(Either.left('error')); - final r = task.run(); - r.match((l) => expect(l, 'error'), (_) { - fail('should be left'); - }); - }); - }); - - group('fromOption', () { - test('Some', () { - final task = - IOEither<String, int>.fromOption(Option.of(10), () => 'none'); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('None', () { - final task = - IOEither<String, int>.fromOption(Option.none(), () => 'none'); - final r = task.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('fromNullable', () { - test('Right', () { - final task = IOEither<String, int>.fromNullable(10, () => "Error"); - final result = task.run(); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () { - final task = IOEither<String, int>.fromNullable(null, () => "Error"); - final result = task.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('fromPredicate', () { - test('True', () { - final task = - IOEither<String, int>.fromPredicate(20, (n) => n > 10, (n) => '$n'); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 20)); - }); - - test('False', () { - final task = - IOEither<String, int>.fromPredicate(10, (n) => n > 10, (n) => '$n'); - final r = task.run(); - r.match((l) => expect(l, '10'), (_) { - fail('should be left'); - }); - }); - }); - - test('fromIO', () { - final task = IOEither<String, int>.fromIO(IO(() => 10)); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('left', () { - final task = IOEither<String, int>.left('none'); - final r = task.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('right', () { - final task = IOEither<String, int>.right(10); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('leftTask', () { - final task = IOEither<String, int>.leftIO(IO(() => 'none')); - final r = task.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('rightTask', () { - final task = IOEither<String, int>.rightIO(IO.of(10)); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - group('match', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ex = task.match((l) => l.length, (r) => r + 10); - final r = ex.run(); - expect(r, 20); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('none')); - final ex = task.match((l) => l.length, (r) => r + 10); - final r = ex.run(); - expect(r, 4); - }); - }); - - group('getOrElse', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ex = task.getOrElse((l) => l.length); - final r = ex.run(); - expect(r, 10); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('none')); - final ex = task.getOrElse((l) => l.length); - final r = ex.run(); - expect(r, 4); - }); - }); - - group('orElse', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ex = task.orElse<int>((l) => IOEither(() => Right(l.length))); - final r = ex.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('none')); - final ex = task.orElse<int>((l) => IOEither(() => Right(l.length))); - final r = ex.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 4)); - }); - }); - - group('alt', () { - test('Right', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ex = task.alt(() => IOEither(() => Either.of(20))); - final r = ex.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final task = IOEither<String, int>(() => Either.left('none')); - final ex = task.alt(() => IOEither(() => Either.of(20))); - final r = ex.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 20)); - }); - }); - - test('swap', () { - final task = IOEither<String, int>(() => Either.of(10)); - final ex = task.swap(); - final r = ex.run(); - r.match((l) => expect(l, 10), (_) { - fail('should be left'); - }); - }); - - test('of', () { - final task = IOEither<String, int>.of(10); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('flatten', () { - final task = IOEither<String, IOEither<String, int>>.of( - IOEither<String, int>.of(10)); - final ap = IOEither.flatten(task); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - }); - - test('chainFirst', () async { - final task = IOEither<String, int>.of(10); - var sideEffect = 10; - final chain = task.chainFirst((b) { - sideEffect = 100; - return IOEither.left("abc"); - }); - final r = await chain.run(); - r.match( - (l) => fail('should be right'), - (r) { - expect(r, 10); - expect(sideEffect, 100); - }, - ); - }); - - group('sequenceList', () { - test('Right', () { - var sideEffect = 0; - final list = [ - IOEither(() { - sideEffect += 1; - return right<String, int>(1); - }), - IOEither(() { - sideEffect += 1; - return right<String, int>(2); - }), - IOEither(() { - sideEffect += 1; - return right<String, int>(3); - }), - IOEither(() { - sideEffect += 1; - return right<String, int>(4); - }), - ]; - final traverse = IOEither.sequenceList(list); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestRight((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, list.length); - }); - - test('Left', () { - var sideEffect = 0; - final list = [ - IOEither(() { - sideEffect += 1; - return right<String, int>(1); - }), - IOEither(() { - sideEffect += 1; - return left<String, int>("Error"); - }), - IOEither(() { - sideEffect += 1; - return right<String, int>(3); - }), - IOEither(() { - sideEffect += 1; - return right<String, int>(4); - }), - ]; - final traverse = IOEither.sequenceList(list); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - - group('traverseList', () { - test('Right', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = IOEither.traverseList<String, int, String>(list, (a) { - sideEffect += 1; - return IOEither.of("$a"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestRight((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, list.length); - }); - - test('Left', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = IOEither.traverseList<String, int, String>(list, (a) { - sideEffect += 1; - return a % 2 == 0 ? IOEither.left("Error") : IOEither.of("$a"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - - group('traverseListWithIndex', () { - test('Right', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = - IOEither.traverseListWithIndex<String, int, String>(list, (a, i) { - sideEffect += 1; - return IOEither.of("$a$i"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestRight((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, list.length); - }); - - test('Left', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = - IOEither.traverseListWithIndex<String, int, String>(list, (a, i) { - sideEffect += 1; - return a % 2 == 0 ? IOEither.left("Error") : IOEither.of("$a$i"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - - group('Do Notation', () { - test('should return the correct value', () { - final doIOEither = IOEither<String, int>.Do((_) => _(IOEither.of(10))); - final run = doIOEither.run(); - run.matchTestRight((t) { - expect(t, 10); - }); - }); - - test('should extract the correct values', () { - final doIOEither = IOEither<String, int>.Do((_) { - final a = _(IOEither.of(10)); - final b = _(IOEither.of(5)); - return a + b; - }); - final run = doIOEither.run(); - run.matchTestRight((t) { - expect(t, 15); - }); - }); - - test('should return Left if any Either is Left', () { - final doIOEither = IOEither<String, int>.Do((_) { - final a = _(IOEither.of(10)); - final b = _(IOEither.of(5)); - final c = _(IOEither<String, int>.left('Error')); - return a + b + c; - }); - final run = doIOEither.run(); - run.matchTestLeft((t) { - expect(t, 'Error'); - }); - }); - - test('should rethrow if throw is used inside Do', () { - final doIOEither = IOEither<String, int>.Do((_) { - _(IOEither.of(10)); - throw UnimplementedError(); - }); - - expect(doIOEither.run, throwsA(const TypeMatcher<UnimplementedError>())); - }); - - test('should rethrow if Left is thrown inside Do', () { - final doIOEither = IOEither<String, int>.Do((_) { - _(IOEither.of(10)); - throw Left('Error'); - }); - - expect(doIOEither.run, throwsA(const TypeMatcher<Left>())); - }); - - test('should no execute past the first Left', () { - var mutable = 10; - final doIOEitherLeft = IOEither<String, int>.Do((_) { - final a = _(IOEither.of(10)); - final b = _(IOEither<String, int>.left("Error")); - mutable += 10; - return a + b; - }); - - final runLeft = doIOEitherLeft.run(); - expect(mutable, 10); - runLeft.matchTestLeft((l) { - expect(l, "Error"); - }); - - final doIOEitherRight = IOEither<String, int>.Do((_) { - final a = _(IOEither.of(10)); - mutable += 10; - return a; - }); - - final runRight = doIOEitherRight.run(); - expect(mutable, 20); - runRight.matchTestRight((t) { - expect(t, 10); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/io_option_test.dart b/packages/fpdart/test/src/io_option_test.dart deleted file mode 100644 index 4a4cd93..0000000 --- a/packages/fpdart/test/src/io_option_test.dart +++ /dev/null @@ -1,579 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import './utils/utils.dart'; - -void main() { - group('IOOption', () { - group('tryCatch', () { - test('Success', () { - final io = IOOption<int>.tryCatch(() => 10); - final r = io.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('throws Exception', () { - final io = IOOption<int>.tryCatch(() { - throw UnimplementedError(); - }); - final r = io.run(); - expect(r, isA<None>()); - }); - }); - - group('tryCatchK', () { - test('Success', () { - final io = IOOption<int>.of(10); - final ap = io.flatMap(IOOption.tryCatchK( - (n) => n + 5, - )); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 15)); - }); - - test('throws Exception', () { - final io = IOOption<int>.of(10); - final ap = io.flatMap(IOOption.tryCatchK<int, int>((_) { - throw UnimplementedError(); - })); - final r = ap.run(); - expect(r, isA<None>()); - }); - }); - - group('flatMap', () { - test('Some', () { - final io = IOOption<int>(() => Option.of(10)); - final ap = io.flatMap((r) => IOOption<int>(() => Option.of(r + 10))); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 20)); - }); - - test('None', () { - final io = IOOption<int>(() => Option.none()); - final ap = io.flatMap((r) => IOOption<int>(() => Option.of(r + 10))); - final r = ap.run(); - expect(r, isA<None>()); - }); - }); - - group('ap', () { - test('Some', () { - final io = IOOption<int>(() => Option.of(10)); - final ap = io.ap<double>(IOOption(() => Option.of((int c) => c / 2))); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 5.0)); - }); - - test('None', () { - final io = IOOption<int>(() => Option.none()); - final ap = io.ap<double>(IOOption(() => Option.of((int c) => c / 2))); - final r = ap.run(); - expect(r, isA<None>()); - }); - }); - - group('map', () { - test('Some', () { - final io = IOOption<int>(() => Option.of(10)); - final ap = io.map((r) => r / 2); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 5.0)); - }); - - test('None', () { - final io = IOOption<int>(() => Option.none()); - final ap = io.map((r) => r / 2); - final r = ap.run(); - expect(r, isA<None>()); - }); - }); - - group('map2', () { - test('Some', () { - final io = IOOption<int>(() => Option.of(10)); - final ap = io.map2<int, double>( - IOOption<int>(() => Option.of(2)), (b, c) => b / c); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 5.0)); - }); - - test('None', () { - final io = IOOption<int>(() => Option.none()); - final ap = io.map2<int, double>( - IOOption<int>(() => Option.of(2)), (b, c) => b / c); - final r = ap.run(); - expect(r, isA<None>()); - }); - }); - - group('map3', () { - test('Some', () { - final io = IOOption<int>(() => Option.of(10)); - final ap = io.map3<int, int, double>(IOOption<int>(() => Option.of(2)), - IOOption<int>(() => Option.of(5)), (b, c, d) => b * c / d); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 4.0)); - }); - - test('None', () { - final io = IOOption<int>(() => Option.none()); - final ap = io.map3<int, int, double>(IOOption<int>(() => Option.of(2)), - IOOption<int>(() => Option.of(5)), (b, c, d) => b * c / d); - final r = ap.run(); - expect(r, isA<None>()); - }); - }); - - group('andThen', () { - test('Some', () { - final io = IOOption<int>(() => Option.of(10)); - final ap = io.andThen(() => IOOption<double>(() => Option.of(12.5))); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 12.5)); - }); - - test('None', () { - final io = IOOption<int>(() => Option.none()); - final ap = io.andThen(() => IOOption<double>(() => Option.of(12.5))); - final r = ap.run(); - expect(r, isA<None>()); - }); - }); - - group('call', () { - test('Some', () { - final io = IOOption<int>(() => Option.of(10)); - final ap = io(IOOption<double>(() => Option.of(12.5))); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 12.5)); - }); - - test('None', () { - final io = IOOption<int>(() => Option.none()); - final ap = io(IOOption<double>(() => Option.of(12.5))); - final r = ap.run(); - expect(r, isA<None>()); - }); - }); - - test('pure', () { - final io = IOOption<int>(() => Option.none()); - final ap = io.pure('abc'); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 'abc')); - }); - - test('run', () { - final io = IOOption<int>(() => Option.of(10)); - final r = io.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - group('fromEither', () { - test('Some', () { - final io = IOOption.fromEither<String, int>(Either.of(10)); - final r = io.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('None', () { - final io = IOOption.fromEither<String, int>(Either.left('none')); - final r = io.run(); - expect(r, isA<None>()); - }); - }); - - group('fromNullable', () { - test('Right', () { - final io = IOOption<int>.fromNullable(10); - final result = io.run(); - result.matchTestSome((r) { - expect(r, 10); - }); - }); - - test('Left', () { - final io = IOOption<int>.fromNullable(null); - final result = io.run(); - expect(result, isA<None>()); - }); - }); - - group('fromPredicate', () { - test('True', () { - final io = IOOption<int>.fromPredicate(20, (n) => n > 10); - final r = io.run(); - r.matchTestSome((r) => expect(r, 20)); - }); - - test('False', () { - final io = IOOption<int>.fromPredicate(10, (n) => n > 10); - final r = io.run(); - expect(r, isA<None>()); - }); - }); - - test('none()', () { - final io = IOOption<int>.none(); - final r = io.run(); - expect(r, isA<None>()); - }); - - test('some()', () { - final io = IOOption<int>.some(10); - final r = io.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - group('match', () { - test('Some', () { - final io = IOOption<int>(() => Option.of(10)); - final ex = io.match(() => -1, (r) => r + 10); - final r = ex.run(); - expect(r, 20); - }); - - test('None', () { - final io = IOOption<int>(() => Option.none()); - final ex = io.match(() => -1, (r) => r + 10); - final r = ex.run(); - expect(r, -1); - }); - }); - - group('getOrElse', () { - test('Some', () { - final io = IOOption<int>(() => Option.of(10)); - final ex = io.getOrElse(() => -1); - final r = ex.run(); - expect(r, 10); - }); - - test('None', () { - final io = IOOption<int>(() => Option.none()); - final ex = io.getOrElse(() => -1); - final r = ex.run(); - expect(r, -1); - }); - }); - - group('orElse', () { - test('Some', () { - final io = IOOption<int>(() => Option.of(10)); - final ex = io.orElse<int>(() => IOOption(() => Option.of(-1))); - final r = ex.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('None', () { - final io = IOOption<int>(() => Option.none()); - final ex = io.orElse<int>(() => IOOption(() => Option.of(-1))); - final r = ex.run(); - r.matchTestSome((r) => expect(r, -1)); - }); - }); - - group('alt', () { - test('Some', () { - final io = IOOption<int>(() => Option.of(10)); - final ex = io.alt(() => IOOption(() => Option.of(20))); - final r = ex.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('None', () { - final io = IOOption<int>(() => Option.none()); - final ex = io.alt(() => IOOption(() => Option.of(20))); - final r = ex.run(); - r.matchTestSome((r) => expect(r, 20)); - }); - }); - - test('of', () { - final io = IOOption<int>.of(10); - final r = io.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('flatten', () { - final io = IOOption<IOOption<int>>.of(IOOption<int>.of(10)); - final ap = IOOption.flatten(io); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - group('toTaskOption', () { - test('Some', () async { - final io = IOOption(() => Option.of(10)); - final convert = io.toTaskOption(); - final r = await convert.run(); - r.matchTestSome((r) { - expect(r, 10); - }); - }); - - test('None', () async { - final io = IOOption(() => const Option.none()); - final convert = io.toTaskOption(); - final r = await convert.run(); - expect(r, isA<None>()); - }); - }); - - group('toOptionEither', () { - test('Some', () { - final io = IOOption(() => Option.of(10)); - final convert = io.toIOEither(() => 'None'); - final r = convert.run(); - r.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('None', () { - final io = IOOption(() => const Option.none()); - final convert = io.toIOEither(() => 'None'); - final r = convert.run(); - r.matchTestLeft((l) { - expect(l, 'None'); - }); - }); - }); - - group('toTaskEither', () { - test('Some', () async { - final io = IOOption(() => Option.of(10)); - final convert = io.toTaskEither(() => 'None'); - final r = await convert.run(); - r.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('None', () async { - final io = IOOption(() => const Option.none()); - final convert = io.toTaskEither(() => 'None'); - final r = await convert.run(); - r.matchTestLeft((l) { - expect(l, 'None'); - }); - }); - }); - - group('sequenceList', () { - test('Some', () { - var sideEffect = 0; - final list = [ - IOOption(() { - sideEffect += 1; - return some(1); - }), - IOOption(() { - sideEffect += 1; - return some(2); - }), - IOOption(() { - sideEffect += 1; - return some(3); - }), - IOOption(() { - sideEffect += 1; - return some(4); - }), - ]; - final traverse = IOOption.sequenceList(list); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestSome((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, list.length); - }); - - test('None', () { - var sideEffect = 0; - final list = [ - IOOption(() { - sideEffect += 1; - return some(1); - }), - IOOption(() { - sideEffect += 1; - return none<int>(); - }), - IOOption(() { - sideEffect += 1; - return some(3); - }), - IOOption(() { - sideEffect += 1; - return some(4); - }), - ]; - final traverse = IOOption.sequenceList(list); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - - group('traverseList', () { - test('Some', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = IOOption.traverseList<int, String>( - list, - (a) => IOOption( - () { - sideEffect += 1; - return some("$a"); - }, - ), - ); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestSome((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, list.length); - }); - - test('None', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = IOOption.traverseList<int, String>( - list, - (a) => IOOption( - () { - sideEffect += 1; - return a % 2 == 0 ? some("$a") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - - group('traverseListWithIndex', () { - test('Some', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = IOOption.traverseListWithIndex<int, String>( - list, - (a, i) => IOOption( - () { - sideEffect += 1; - return some("$a$i"); - }, - ), - ); - expect(sideEffect, 0); - final result = traverse.run(); - result.matchTestSome((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, list.length); - }); - - test('None', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = IOOption.traverseListWithIndex<int, String>( - list, - (a, i) => IOOption( - () { - sideEffect += 1; - return a % 2 == 0 ? some("$a$i") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - - group('Do Notation', () { - test('should return the correct value', () { - final doIOOption = IOOption<int>.Do((_) => _(IOOption.of(10))); - final run = doIOOption.run(); - run.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('should extract the correct values', () { - final doIOOption = IOOption<int>.Do((_) { - final a = _(IOOption.of(10)); - final b = _(IOOption.of(5)); - return a + b; - }); - final run = doIOOption.run(); - run.matchTestSome((t) { - expect(t, 15); - }); - }); - - test('should return Left if any Either is Left', () { - final doIOOption = IOOption<int>.Do((_) { - final a = _(IOOption.of(10)); - final b = _(IOOption.of(5)); - final c = _(IOOption<int>.none()); - return a + b + c; - }); - final run = doIOOption.run(); - expect(run, isA<None>()); - }); - - test('should rethrow if throw is used inside Do', () { - final doIOOption = IOOption<int>.Do((_) { - _(IOOption.of(10)); - throw UnimplementedError(); - }); - - expect( - doIOOption.run, throwsA(const TypeMatcher<UnimplementedError>())); - }); - - test('should rethrow if None is thrown inside Do', () { - final doIOOption = IOOption<int>.Do((_) { - _(IOOption.of(10)); - throw const None(); - }); - - expect(doIOOption.run, throwsA(const TypeMatcher<None>())); - }); - - test('should no execute past the first Left', () { - var mutable = 10; - final doIOOptionNone = IOOption<int>.Do((_) { - final a = _(IOOption.of(10)); - final b = _(IOOption<int>.none()); - mutable += 10; - return a + b; - }); - - final runNone = doIOOptionNone.run(); - expect(mutable, 10); - expect(runNone, isA<None>()); - - final doIOOptionSome = IOOption<int>.Do((_) { - final a = _(IOOption.of(10)); - mutable += 10; - return a; - }); - - final runSome = doIOOptionSome.run(); - expect(mutable, 20); - runSome.matchTestSome((t) { - expect(t, 10); - }); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/io_ref_test.dart b/packages/fpdart/test/src/io_ref_test.dart deleted file mode 100644 index bf6a51f..0000000 --- a/packages/fpdart/test/src/io_ref_test.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/expect.dart'; -import 'package:test/scaffolding.dart'; - -extension on bool { - void isTrue() => expect(this, true); -} - -void main() { - group("IORef", () { - const value = 1; - - const method = "Method"; - const function = "Function"; - const mEqualsF = "$method equals $function"; - - group("Eq", () { - final first = IORef.create(value); - final second = IORef.create(value); - - test("Pointer equality passes on allocated IORef", () { - final unwrapped = first.run(); - ioRefEq.eqv(unwrapped, unwrapped).isTrue(); - }); - - test( - "Pointer equality fails on two allocations of the same wrapped ref", - () => ioRefEq.neqv(first.run(), first.run()).isTrue(), - ); - test("Pointer equality fails on two different unwrapped refs", () { - final unwrappedFirst = first.run(); - final unwrappedSecond = second.run(); - ioRefEq.neqv(unwrappedFirst, unwrappedSecond).isTrue(); - }); - }); - - group("Read", () { - final ref = IORef.create(value); - - final methodRead = ref.flatMap((ref) => ref.read()).run; - final functionRead = ref.flatMap(readIORef).run; - - test(method, () => expect(methodRead(), value)); - - test(function, () => expect(functionRead(), value)); - - test(mEqualsF, () => expect(methodRead(), functionRead())); - }); - - group( - "Write", - () { - const writingValue = 10; - - final methodWrite = IORef.create(value) - .flatMap( - (ref) => ref.write(writingValue).flatMap((_) => ref.read()), - ) - .run; - - final functionWrite = IORef.create(value) - .flatMap( - (ref) => writeIORef(writingValue, ref).flatMap((_) => ref.read()), - ) - .run; - - test(method, () => expect(methodWrite(), writingValue)); - - test(function, () => expect(functionWrite(), writingValue)); - - test(mEqualsF, () => expect(methodWrite(), functionWrite())); - }, - ); - - group("Modify", () { - int modifyFunction(int value) => value + 4; - final expectedValue = modifyFunction(value); - - final methodModify = IORef.create(value) - .flatMap( - (ref) => ref.modify(modifyFunction).flatMap((_) => ref.read()), - ) - .run; - - final functionModify = IORef.create(value) - .flatMap( - (ref) => - modifyIORef(modifyFunction, ref).flatMap((_) => ref.read()), - ) - .run; - - test(method, () => expect(methodModify(), expectedValue)); - - test(function, () => expect(functionModify(), expectedValue)); - - test(mEqualsF, () => expect(methodModify(), functionModify())); - }); - }); -} diff --git a/packages/fpdart/test/src/io_test.dart b/packages/fpdart/test/src/io_test.dart deleted file mode 100644 index 30b52f6..0000000 --- a/packages/fpdart/test/src/io_test.dart +++ /dev/null @@ -1,222 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import 'utils/utils.dart'; - -void main() { - group('IO', () { - group('is a', () { - final io = IO(() => 10); - - test('Monad', () { - expect(io, isA<Monad>()); - }); - - test('Applicative', () { - expect(io, isA<Applicative>()); - }); - - test('Functor', () { - expect(io, isA<Functor>()); - }); - }); - - test('flatMap', () { - final io = IO(() => 10); - final ap = io.flatMap((a) => IO(() => a + 10)); - final r = ap.run(); - expect(r, 20); - }); - - test('flatMapTask', () async { - final io = IO(() => 10); - final ap = io.flatMapTask((a) => Task(() async => a + 10)); - final r = await ap.run(); - expect(r, 20); - }); - - test('toIOEither', () { - final io = IO(() => 10); - final ap = io.toIOEither<String>(); - final r = ap.run(); - r.matchTestRight((r) => expect(r, 10)); - }); - - test('toTask', () async { - final io = IO(() => 10); - final ap = io.toTask(); - final r = await ap.run(); - expect(r, 10); - }); - - test('toTaskEither', () async { - final io = IO(() => 10); - final ap = io.toTaskEither<String>(); - final r = await ap.run(); - r.matchTestRight((r) => expect(r, 10)); - }); - - test('toTaskOption', () async { - final io = IO(() => 10); - final ap = io.toTaskOption(); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('toIOOption', () { - final io = IO(() => 10); - final ap = io.toIOOption(); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('ap', () { - final io = IO(() => 10); - final ap = io.ap(IO(() => (int a) => a * 3)); - final r = ap.run(); - expect(r, 30); - }); - - test('pure', () { - final io = IO(() => 10); - final ap = io.pure('abc'); - final r = ap.run(); - expect(r, 'abc'); - }); - - test('map', () { - final io = IO(() => 10); - final ap = io.map((a) => '$a'); - final r = ap.run(); - expect(r, '10'); - }); - - test('map2', () { - final io = IO(() => 10); - final ap = io.map2<String, int>(IO(() => 'abc'), (a, c) => a + c.length); - final r = ap.run(); - expect(r, 13); - }); - - test('map3', () { - final io = IO(() => 10); - final ap = io.map3<String, double, double>( - IO(() => 'ab'), IO(() => 0.5), (a, c, d) => (a + c.length) * d); - final r = ap.run(); - expect(r, 6.0); - }); - - test('andThen', () { - final io = IO(() => 10); - final ap = io.andThen(() => IO(() => 'abc')); - final r = ap.run(); - expect(r, 'abc'); - }); - - test('call', () { - final io = IO(() => 10); - final ap = io(IO(() => 'abc')); - final r = ap.run(); - expect(r, 'abc'); - }); - - test('flatten', () { - final io = IO(() => IO(() => 10)); - final ap = IO.flatten(io); - expect(ap, isA<IO<int>>()); - final r = ap.run(); - expect(r, 10); - }); - - test('run', () { - final io = IO(() => 10); - final r = io.run(); - expect(r, isA<int>()); - expect(r, 10); - }); - - test('sequenceList', () { - var sideEffect = 0; - final list = [ - IO(() { - sideEffect += 1; - return 1; - }), - IO(() { - sideEffect += 1; - return 2; - }), - IO(() { - sideEffect += 1; - return 3; - }), - IO(() { - sideEffect += 1; - return 4; - }) - ]; - final traverse = IO.sequenceList(list); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, [1, 2, 3, 4]); - expect(sideEffect, list.length); - }); - - test('traverseList', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = IO.traverseList<int, String>(list, (a) { - sideEffect += 1; - return IO.of("$a"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, ['1', '2', '3', '4', '5', '6']); - expect(sideEffect, list.length); - }); - - test('traverseListWithIndex', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = IO.traverseListWithIndex<int, String>(list, (a, i) { - sideEffect += 1; - return IO.of("$a$i"); - }); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, ['10', '21', '32', '43', '54', '65']); - expect(sideEffect, list.length); - }); - - group('Do Notation', () { - test('should return the correct value', () { - final doIO = IO.Do((_) => _(IO.of(10))); - final run = doIO.run(); - expect(run, 10); - }); - - test('should extract the correct values', () { - final doIO = IO.Do((_) { - final a = _(IO.of(10)); - final b = _(IO.of(5)); - return a + b; - }); - final run = doIO.run(); - expect(run, 15); - }); - - test('should not execute until run is called', () { - var mutable = 10; - final doIO = IO.Do((_) { - final a = _(IO.of(10)); - final b = _(IO.of(5)); - mutable += 10; - return a + b; - }); - expect(mutable, 10); - final run = doIO.run(); - expect(mutable, 20); - expect(run, 15); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/monoid_test.dart b/packages/fpdart/test/src/monoid_test.dart deleted file mode 100644 index 5fb2bad..0000000 --- a/packages/fpdart/test/src/monoid_test.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('Monoid', () { - group('is a', () { - final instance = Monoid.instance<int>(0, (a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA<Semigroup>()); - }); - }); - - test('.instance (int)', () { - final instance = Monoid.instance<int>(0, (a1, a2) => a1 + a2); - expect(instance.combine(10, instance.empty), - instance.combine(instance.empty, 10)); - expect(instance.combine(10, instance.empty), 10); - }); - - test('.instance (String)', () { - final instance = Monoid.instance<String>('', (a1, a2) => '$a1$a2'); - expect(instance.combine('abc', instance.empty), - instance.combine(instance.empty, 'abc')); - expect(instance.combine('abc', instance.empty), 'abc'); - }); - - test('.isEmpty', () { - final instance = Monoid.instance<int>(0, (a1, a2) => a1 + a2); - final eq = Eq.instance<int>((a1, a2) => a1 == a2); - expect(instance.isEmpty(instance.empty, eq), true); - expect(instance.isEmpty(0, eq), true); - expect(instance.isEmpty(1, eq), false); - }); - - test('.combineN', () { - final instance = Monoid.instance<int>(0, (a1, a2) => a1 + a2); - expect(instance.combineN(0, 10), 0); - expect(instance.combineN(1, 10), 10); - }); - - test('.reverse', () { - final instance = Monoid.instance<String>('', (a1, a2) => '$a1$a2'); - final reverse = instance.reverse(); - expect(reverse.combine('a', 'b'), 'ba'); - expect(reverse.combine('a', 'b'), instance.combine('b', 'a')); - }); - }); -} diff --git a/packages/fpdart/test/src/option_test.dart b/packages/fpdart/test/src/option_test.dart deleted file mode 100644 index 1de5ea3..0000000 --- a/packages/fpdart/test/src/option_test.dart +++ /dev/null @@ -1,811 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import './utils/utils.dart'; - -void main() { - group('Option', () { - group('[Property-based testing]', () { - group("safeCast", () { - Glados2(any.int, any.letterOrDigits) - .test('always returns Some without typed parameter', - (intValue, stringValue) { - final castInt = Option.safeCast(intValue); - final castString = Option.safeCast(stringValue); - expect(castInt, isA<Some<dynamic>>()); - expect(castString, isA<Some<dynamic>>()); - }); - }); - - group('map', () { - Glados(any.optionInt).test('should keep the same type (Some or None)', - (option) { - final r = option.map(constF); - expect(option.isSome(), r.isSome()); - expect(option.isNone(), r.isNone()); - }); - - Glados2(any.optionInt, any.int) - .test('should updated the value inside Some, or stay None', - (option, value) { - final r = option.map((n) => n + value); - option.match( - () { - expect(option, r); - }, - (val1) { - r.matchTestSome((val2) { - expect(val2, val1 + value); - }); - }, - ); - }); - }); - - group('traverseList', () { - Glados(any.list(any.int)).test( - 'should keep the same structure and content of the original list', - (input) { - final result = Option.traverseList(input, Option<int>.of); - result.matchTestSome((t) { - expect(t, input); - }); - }); - }); - }); - - group('is a', () { - final option = Option.of(10); - - test('Monad', () { - expect(option, isA<Monad>()); - }); - - test('Applicative', () { - expect(option, isA<Applicative>()); - }); - - test('Functor', () { - expect(option, isA<Functor>()); - }); - - test('Extend', () { - expect(option, isA<Extend>()); - }); - - test('Filterable', () { - expect(option, isA<Filterable>()); - }); - }); - - test('map', () { - final option = Option.of(10); - final map = option.map((a) => a + 1); - map.matchTestSome((some) => expect(some, 11)); - }); - - test('map2', () { - final option = Option.of(10); - final map = - option.map2<String, int>(Option.of('abc'), (a, b) => a + b.length); - map.matchTestSome((some) => expect(some, 13)); - }); - - test('map3', () { - final option = Option.of(10); - final map = option.map3<String, double, double>( - Option.of('abc'), Option.of(2.0), (a, b, c) => (a + b.length) / c); - map.matchTestSome((some) => expect(some, 6.5)); - }); - - group('ap', () { - test('Some', () { - final option = Option.of(10); - final pure = option.ap(Option.of((int i) => i + 1)); - pure.matchTestSome((some) => expect(some, 11)); - }); - - test('Some (curried)', () { - final ap = Option.of((int a) => (int b) => a + b) - .ap( - Option.of((f) => f(3)), - ) - .ap( - Option.of((f) => f(5)), - ); - ap.matchTestSome((some) => expect(some, 8)); - }); - - test('None', () { - final option = Option<int>.none(); - final pure = option.ap(Option.of((int i) => i + 1)); - expect(pure, isA<None>()); - }); - }); - - group('flatMap', () { - test('Some', () { - final option = Option.of(10); - final flatMap = option.flatMap<int>((a) => Option.of(a + 1)); - flatMap.matchTestSome((some) => expect(some, 11)); - }); - - test('None', () { - final option = Option<int>.none(); - final flatMap = option.flatMap<int>((a) => Option.of(a + 1)); - expect(flatMap, isA<None>()); - }); - }); - - group('flatMapNullable', () { - test('not null', () { - final option = Option.of(10); - final flatMap = option.flatMapNullable((a) => a + 1); - flatMap.matchTestSome((some) => expect(some, 11)); - }); - - test('null', () { - final option = Option.of(10); - final flatMap = option.flatMapNullable<int>((a) => null); - expect(flatMap, isA<None>()); - }); - }); - - group('flatMapThrowable', () { - test('happy path', () { - final option = Option.of(10); - final flatMap = option.flatMapThrowable((a) => a + 1); - flatMap.matchTestSome((some) => expect(some, 11)); - }); - - test('throws', () { - final option = Option.of(10); - final flatMap = option.flatMapThrowable<int>((a) => throw "fail"); - expect(flatMap, isA<None>()); - }); - }); - - group('extend', () { - test('Some', () { - final option = Option.of(10); - final value = option.extend((t) => t.isSome() ? 'valid' : 'invalid'); - value.matchTestSome((some) => expect(some, 'valid')); - }); - - test('None', () { - final option = Option<int>.none(); - final value = option.extend((t) => t.isSome() ? 'valid' : 'invalid'); - expect(value, isA<None>()); - }); - }); - - group('duplicate', () { - test('Some', () { - final option = Option.of(10); - final value = option.duplicate(); - value.matchTestSome((some) => expect(some, isA<Some>())); - }); - - test('None', () { - final option = Option<int>.none(); - final value = option.duplicate(); - expect(value, isA<None>()); - }); - }); - - group('filter', () { - test('Some (true)', () { - final option = Option.of(10); - final value = option.filter((a) => a > 5); - value.matchTestSome((some) => expect(some, 10)); - }); - - test('Some (false)', () { - final option = Option.of(10); - final value = option.filter((a) => a < 5); - expect(value, isA<None>()); - }); - - test('None', () { - final option = Option<int>.none(); - final value = option.filter((a) => a > 5); - expect(value, isA<None>()); - }); - }); - - group('filterMap', () { - test('Some', () { - final option = Option.of(10); - final value = option.filterMap<String>((a) => Option.of('$a')); - value.matchTestSome((some) => expect(some, '10')); - }); - - test('None', () { - final option = Option<int>.none(); - final value = option.filterMap<String>((a) => Option.of('$a')); - expect(value, isA<None>()); - }); - }); - - group('partition', () { - test('Some (true)', () { - final option = Option.of(10); - final value = option.partition((a) => a > 5); - expect(value.$1, isA<None>()); - value.$2.matchTestSome((some) => expect(some, 10)); - }); - - test('Some (false)', () { - final option = Option.of(10); - final value = option.partition((a) => a < 5); - value.$1.matchTestSome((some) => expect(some, 10)); - expect(value.$2, isA<None>()); - }); - - test('None', () { - final option = Option<int>.none(); - final value = option.partition((a) => a > 5); - expect(value.$1, isA<None>()); - expect(value.$2, isA<None>()); - }); - }); - - group('partitionMap', () { - test('Some (right)', () { - final option = Option.of(10); - final value = - option.partitionMap<String, double>((a) => Either.of(a / 2)); - expect(value.$1, isA<None>()); - value.$2.matchTestSome((some) => expect(some, 5.0)); - }); - - test('Some (left)', () { - final option = Option.of(10); - final value = - option.partitionMap<String, double>((a) => Either.left('$a')); - value.$1.matchTestSome((some) => expect(some, '10')); - expect(value.$2, isA<None>()); - }); - - test('None', () { - final option = Option<int>.none(); - final value = - option.partitionMap<String, double>((a) => Either.of(a / 2)); - expect(value.$1, isA<None>()); - expect(value.$2, isA<None>()); - }); - }); - - group('fromEither', () { - test('Right', () { - final option = Option.fromEither<String, int>(Either.of(10)); - option.matchTestSome((some) => expect(some, 10)); - }); - - test('Left', () { - final option = Option.fromEither<String, int>(Either.left('none')); - expect(option, isA<None>()); - }); - }); - - group('fromJson', () { - test('int', () { - final option = Option<int>.fromJson(10, (a) => a as int); - option.matchTestSome((some) => expect(some, 10)); - }); - - test('DateTime', () { - final now = DateTime.now(); - final option = Option<DateTime>.fromJson( - now.toIso8601String(), (a) => DateTime.parse(a as String)); - option.matchTestSome((some) => expect(some, now)); - }); - - test('DateTime failure', () { - final option = Option<DateTime>.fromJson( - "fail", (a) => DateTime.parse(a as String)); - expect(option, isA<None>()); - }); - - test('null', () { - final option = Option<int>.fromJson(null, (a) => a as int); - expect(option, isA<None>()); - }); - }); - - group('fromPredicate', () { - test('Some', () { - final option = Option<int>.fromPredicate(10, (a) => a > 5); - option.matchTestSome((some) => expect(some, 10)); - }); - - test('None', () { - final option = Option<int>.fromPredicate(10, (a) => a < 5); - expect(option, isA<None>()); - }); - }); - - group('fromPredicateMap', () { - test('Some', () { - final option = - Option.fromPredicateMap<int, String>(10, (a) => a > 5, (a) => '$a'); - option.matchTestSome((some) => expect(some, '10')); - }); - - test('None', () { - final option = - Option.fromPredicateMap<int, String>(10, (a) => a < 5, (a) => '$a'); - expect(option, isA<None>()); - }); - }); - - group('flatten', () { - test('Right', () { - final option = Option.flatten(Option.of(Option.of(10))); - option.matchTestSome((some) => expect(some, 10)); - }); - - test('Left', () { - final option = Option.flatten(Option.of(Option<int>.none())); - expect(option, isA<None>()); - }); - }); - - group('separate', () { - test('Right', () { - final option = Option.separate<String, int>(Option.of(Either.of(10))); - expect(option.$1, isA<None>()); - option.$2.matchTestSome((some) => expect(some, 10)); - }); - - test('Left', () { - final option = - Option.separate<String, int>(Option.of(Either.left('none'))); - option.$1.matchTestSome((some) => expect(some, 'none')); - expect(option.$2, isA<None>()); - }); - }); - - test('None', () { - final option = Option<int>.none(); - expect(option, isA<None>()); - }); - - test('of', () { - final option = Option.of(10); - option.matchTestSome((some) => expect(some, 10)); - }); - - test('isSome', () { - final option = Option.of(10); - expect(option.isSome(), true); - expect(option.isNone(), false); - }); - - test('isNone', () { - final option = Option<int>.none(); - expect(option.isNone(), true); - expect(option.isSome(), false); - }); - - test('getEq', () { - final eq = Option.getEq<int>(Eq.instance((a1, a2) => a1 == a2)); - expect(eq.eqv(Option.of(10), Option.of(10)), true); - expect(eq.eqv(Option.of(10), Option.of(9)), false); - expect(eq.eqv(Option.of(10), Option<int>.none()), false); - expect(eq.eqv(Option<int>.none(), Option<int>.none()), true); - }); - - test('getOrder', () { - final order = - Option.getOrder<int>(Order.from((a1, a2) => a1.compareTo(a2))); - final option1 = Option.of(10); - final option2 = Option.of(9); - final option3 = Option<int>.none(); - expect(order.compare(option1, option1), 0); - expect(order.compare(option3, option3), 0); - expect(order.compare(option1, option2), 1); - expect(order.compare(option2, option1), -1); - expect(order.compare(option1, option3), 1); - expect(order.compare(option3, option1), -1); - }); - - test('fromNullable', () { - final m1 = Option<int>.fromNullable(10); - final m2 = Option<int>.fromNullable(null); - expect(m1, isA<Some>()); - expect(m2, isA<None>()); - }); - - test('tryCatch', () { - final m1 = Option.tryCatch(() => 10); - final m2 = Option.tryCatch(() => throw UnimplementedError()); - expect(m1, isA<Some>()); - expect(m2, isA<None>()); - }); - - test('toEither', () { - final m1 = Option.of(10); - final m2 = Option<int>.none(); - final e1 = m1.toEither(() => 'left'); - final e2 = m2.toEither(() => 'left'); - e1.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - e2.match((l) => expect(l, 'left'), (_) { - fail('should be left'); - }); - }); - - test('toNullable', () { - final m1 = Option.of(10); - final m2 = Option<int>.none(); - expect(m1.toNullable(), 10); - expect(m1.toNullable(), isA<int?>()); - expect(m2.toNullable(), null); - }); - - test('pure', () { - final m1 = Option.of(10); - final m2 = Option<int>.none(); - m1.pure('abc').matchTestSome((some) => expect(some, 'abc')); - m2.pure('abc').matchTestSome((some) => expect(some, 'abc')); - }); - - test('andThen', () { - final m1 = Option.of(10); - final m2 = Option<int>.none(); - m1 - .andThen(() => Option.of('abc')) - .matchTestSome((some) => expect(some, 'abc')); - expect(m2.andThen(() => Option.of('abc')), isA<None>()); - }); - - test('call', () { - final m1 = Option.of(10); - final m2 = Option<int>.none(); - m1(Option.of('abc')).matchTestSome((some) => expect(some, 'abc')); - expect(m2(Option.of('abc')), isA<None>()); - }); - - test('match', () { - final m1 = Option.of(10); - final m2 = Option<int>.none(); - expect(m1.match(() => 'none', (some) => 'some'), 'some'); - expect(m2.match(() => 'none', (some) => 'some'), 'none'); - }); - - test('match', () { - final m1 = Option.of(10); - final m2 = Option<int>.none(); - expect(m1.fold(() => 'none', (some) => 'some'), 'some'); - expect(m2.fold(() => 'none', (some) => 'some'), 'none'); - }); - - test('none()', () { - final m = Option<int>.none(); - expect(m, isA<None>()); - }); - - test('of()', () { - final m = Option.of(10); - expect(m, isA<Some<int>>()); - }); - - test('getFirstMonoid', () { - final m = Option.getFirstMonoid<int>(); - expect(m.empty, isA<None>()); - m - .combine(Option.of(10), Option.of(0)) - .matchTestSome((some) => expect(some, 10)); - m - .combine(Option.none(), Option.of(0)) - .matchTestSome((some) => expect(some, 0)); - }); - - test('getLastMonoid', () { - final m = Option.getLastMonoid<int>(); - expect(m.empty, isA<None>()); - m - .combine(Option.of(10), Option.of(0)) - .matchTestSome((some) => expect(some, 0)); - m - .combine(Option.of(10), Option.none()) - .matchTestSome((some) => expect(some, 10)); - }); - - test('getMonoid', () { - final m = Option.getMonoid<int>(Semigroup.instance((a1, a2) => a1 + a2)); - expect(m.empty, isA<None>()); - m - .combine(Option.of(10), Option.of(20)) - .matchTestSome((some) => expect(some, 30)); - expect(m.combine(Option.of(10), Option.none()), isA<None>()); - expect(m.combine(Option.none(), Option.of(10)), isA<None>()); - }); - - group('toString', () { - test('Some', () { - final m = Option.of(10); - expect(m.toString(), 'Some(10)'); - }); - - test('None', () { - final m = Option<int>.none(); - expect(m.toString(), 'None'); - }); - }); - - group('sequenceList', () { - test('Some', () { - final list = [some(1), some(2), some(3), some(4)]; - final result = Option.sequenceList(list); - result.matchTestSome((t) { - expect(t, [1, 2, 3, 4]); - }); - }); - - test('None', () { - final list = [some(1), none<int>(), some(3), some(4)]; - final result = Option.sequenceList(list); - expect(result, isA<None>()); - }); - }); - - group('traverseList', () { - test('Some', () { - final list = [1, 2, 3, 4, 5, 6]; - final result = - Option.traverseList<int, String>(list, (a) => some("$a")); - result.matchTestSome((t) { - expect(t, ["1", "2", "3", "4", "5", "6"]); - }); - }); - - test('None', () { - final list = [1, 2, 3, 4, 5, 6]; - final result = Option.traverseList<int, String>( - list, - (a) => a % 2 == 0 ? some("$a") : none(), - ); - expect(result, isA<None>()); - }); - }); - - group('traverseListWithIndex', () { - test('Some', () { - final list = [1, 2, 3, 4, 5, 6]; - final result = Option.traverseListWithIndex<int, String>( - list, (a, i) => some("$a$i")); - result.matchTestSome((t) { - expect(t, ["10", "21", "32", "43", "54", "65"]); - }); - }); - - test('None', () { - final list = [1, 2, 3, 4, 5, 6]; - final result = Option.traverseListWithIndex<int, String>( - list, - (a, i) => i % 2 == 0 ? some("$a$i") : none(), - ); - expect(result, isA<None>()); - }); - }); - - group('safeCast', () { - test('dynamic', () { - final castInt = Option.safeCast(10); - final castString = Option.safeCast('abc'); - expect(castInt, isA<Some<dynamic>>()); - expect(castString, isA<Some<dynamic>>()); - }); - - test('Some', () { - final cast = Option<int>.safeCast(10); - cast.matchTestSome((r) { - expect(r, 10); - }); - }); - - test('None', () { - final cast = Option<int>.safeCast('abc'); - expect(cast, isA<None>()); - }); - }); - - group('toTaskOption', () { - test('Some', () async { - final m = Option.of(10); - final taskOption = m.toTaskOption(); - final result = await taskOption.run(); - expect(result, m); - }); - - test('None', () async { - final m = Option<int>.none(); - final taskOption = m.toTaskOption(); - final result = await taskOption.run(); - expect(result, m); - }); - }); - - group('toIOOption', () { - test('Some', () async { - final m = Option.of(10); - final ioOption = m.toIOOption(); - final result = ioOption.run(); - expect(result, m); - }); - - test('None', () { - final m = Option<int>.none(); - final ioOption = m.toIOOption(); - final result = ioOption.run(); - expect(result, m); - }); - }); - - test('Some value', () { - const m = Some(10); - expect(m.value, 10); - }); - - test('Some == Some', () { - final m1 = Option.of(10); - final m2 = Option.of(9); - final m3 = Option<int>.none(); - final m4 = Option.of(10); - final map1 = <String, Option>{'m1': m1, 'm2': m4}; - final map2 = <String, Option>{'m1': m1, 'm2': m2}; - final map3 = <String, Option>{'m1': m1, 'm2': m4}; - expect(m1, m1); - expect(m2, m2); - expect(m1, m4); - expect(m1 == m2, false); - expect(m4 == m2, false); - expect(m1 == m3, false); - expect(m2 == m3, false); - expect(map1, map1); - expect(map1, map3); - expect(map1 == map2, false); - }); - - test('None == None', () { - final m1 = Option.of(10); - final m2 = Option.of(9); - final m3 = Option<int>.none(); - final m4 = Option<int>.none(); - final m5 = Option<String>.none(); - final map1 = <String, Option>{'m1': m3, 'm2': m3}; - final map2 = <String, Option>{'m1': m3, 'm2': m4}; - final map3 = <String, Option>{'m1': m3, 'm2': m5}; - final map4 = <String, Option>{'m1': m3, 'm2': m1}; - expect(m3, m3); - expect(m3, m4); - expect(m5, m5); - expect(m3, m5); - expect(m1 == m3, false); - expect(m2 == m3, false); - expect(map1, map1); - expect(map1, map2); - expect(map1, map3); - expect(map1 == map4, false); - }); - }); - - group('safeCastStrict', () { - test('Some', () { - final cast = Option.safeCastStrict<int, int>(10); - cast.matchTestSome((r) { - expect(r, 10); - }); - }); - - test('None', () { - final cast = Option.safeCastStrict<int, String>('abc'); - expect(cast, isA<None>()); - }); - }); - - group('Do Notation', () { - test('should traverse over a list', () async { - const testOption = const Option<List<String?>>.of( - ['/', '/test', null], - ); - - Option<List<Uri>> doNotation = Option.Do( - ($) { - List<String?> optionList = $(testOption); - return $(optionList.traverseOption( - (stringValue) => optionOf(stringValue).flatMap( - (uri) => optionOf( - Uri.tryParse(uri), - ), - ), - )); - }, - ); - - expect(doNotation, equals(const Option<Uri>.none())); - }); - - test('should return the correct value', () { - final doOption = Option.Do((_) => _(Option.of(10))); - doOption.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('should extract the correct values', () { - final doOption = Option.Do((_) { - final a = _(Option.of(10)); - final b = _(Option.of(5)); - return a + b; - }); - doOption.matchTestSome((t) { - expect(t, 15); - }); - }); - - test('should return None if any Option is None', () { - final doOption = Option.Do((_) { - final a = _(Option.of(10)); - final b = _(Option.of(5)); - final c = _(Option<int>.none()); - return a + b + c; - }); - - expect(doOption, isA<None>()); - }); - - test('should rethrow if throw is used inside Do', () { - final doOption = () => Option.Do((_) { - _(Option.of(10)); - throw UnimplementedError(); - }); - - expect(doOption, throwsA(const TypeMatcher<UnimplementedError>())); - }); - - test('should rethrow if None is thrown inside Do', () { - final doOption = () => Option.Do((_) { - _(Option.of(10)); - throw None(); - }); - - expect(doOption, throwsA(const TypeMatcher<None>())); - }); - - test('should throw if the error is not None', () { - final doOption = () => Option.Do((_) { - _(Option.of(10)); - throw UnimplementedError(); - }); - - expect(doOption, throwsA(const TypeMatcher<UnimplementedError>())); - }); - - test('should no execute past the first None', () { - var mutable = 10; - final doOptionNone = Option.Do((_) { - final a = _(Option.of(10)); - final b = _(Option<int>.none()); - mutable += 10; - return a + b; - }); - - expect(mutable, 10); - expect(doOptionNone, isA<None>()); - - final doOptionSome = Option.Do((_) { - final a = _(Option.of(10)); - mutable += 10; - return a; - }); - - expect(mutable, 20); - doOptionSome.matchTestSome((t) { - expect(t, 10); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/order_test.dart b/packages/fpdart/test/src/order_test.dart deleted file mode 100644 index 500568b..0000000 --- a/packages/fpdart/test/src/order_test.dart +++ /dev/null @@ -1,240 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -class _Parent { - final int value1; - final double value2; - const _Parent(this.value1, this.value2); -} - -void main() { - group('Order', () { - group('is a', () { - final instance = Order.from<int>((a1, a2) => a1.compareTo(a2)); - - test('Eq', () { - expect(instance, isA<Eq>()); - }); - - test('PartialOrder', () { - expect(instance, isA<PartialOrder>()); - }); - }); - - test('.from', () { - final instance = Order.from<int>((a1, a2) => a1.compareTo(a2)); - expect(instance.compare(1, 1), 0); - expect(instance.compare(1, 2), -1); - expect(instance.compare(2, 1), 1); - }); - - test('reverse', () { - final instance = Order.orderInt; - final reverse = instance.reverse; - expect(reverse.compare(1, 1), 0); - expect(reverse.compare(1, 2), 1); - expect(reverse.compare(2, 1), -1); - }); - - test('.fromLessThan', () { - final instance = Order.fromLessThan<int>((a1, a2) => a1 < a2); - expect(instance.compare(1, 1), 0); - expect(instance.compare(1, 2), -1); - expect(instance.compare(2, 1), 1); - }); - - test('.allEqual', () { - final instance = Order.allEqual<int>(); - expect(instance.compare(1, 1), 0); - expect(instance.compare(1, 2), 0); - expect(instance.compare(2, 1), 0); - }); - - test('.whenEqual', () { - final instance1 = - Order.fromLessThan<String>((a1, a2) => a1.length < a2.length); - final instance2 = Order.from<String>( - (a1, a2) => a1.substring(0, 1).compareTo(a2.substring(0, 1))); - final whenEqual = Order.whenEqual(instance1, instance2); - expect(whenEqual.compare('abc', 'abcd'), -1); - expect(whenEqual.compare('abcd', 'abc'), 1); - expect(whenEqual.compare('abc', 'ebc'), -1); - expect(whenEqual.compare('ebc', 'abc'), 1); - expect(whenEqual.compare('abc', 'abc'), 0); - }); - - test('.by', () { - final instance = Order.from<int>((a1, a2) => a1.compareTo(a2)); - final by = Order.by<String, int>((a1) => a1.length, instance); - expect(by.compare('abc', 'abc'), 0); - expect(by.compare('abc', 'abcd'), -1); - expect(by.compare('abcd', 'abc'), 1); - }); - - test('min', () { - final instance = Order.from<int>((a1, a2) => a1.compareTo(a2)); - expect(instance.min(0, 10), 0); - expect(instance.min(10, 0), 0); - }); - - test('max', () { - final instance = Order.from<int>((a1, a2) => a1.compareTo(a2)); - expect(instance.max(0, 10), 10); - expect(instance.max(10, 0), 10); - }); - - test('eqv', () { - final instance = Order.from<int>((a1, a2) => a1.compareTo(a2)); - expect(instance.eqv(0, 10), false); - expect(instance.eqv(0, 0), true); - }); - - test('neqv', () { - final instance = Order.from<int>((a1, a2) => a1.compareTo(a2)); - expect(instance.neqv(0, 10), true); - expect(instance.neqv(0, 0), false); - }); - - test('lteqv', () { - final instance = Order.from<int>((a1, a2) => a1.compareTo(a2)); - expect(instance.lteqv(0, 10), true); - expect(instance.lteqv(0, 0), true); - expect(instance.lteqv(0, -1), false); - }); - - test('lt', () { - final instance = Order.from<int>((a1, a2) => a1.compareTo(a2)); - expect(instance.lt(0, 10), true); - expect(instance.lt(0, 0), false); - expect(instance.lt(0, -1), false); - }); - - test('gteqv', () { - final instance = Order.from<int>((a1, a2) => a1.compareTo(a2)); - expect(instance.gteqv(0, 10), false); - expect(instance.gteqv(0, 0), true); - expect(instance.gteqv(0, -1), true); - }); - - test('gt', () { - final instance = Order.from<int>((a1, a2) => a1.compareTo(a2)); - expect(instance.gt(0, 10), false); - expect(instance.gt(0, 0), false); - expect(instance.gt(0, -1), true); - }); - - test('between', () { - final instance = Order.orderInt; - expect(instance.between(0, 10, 4), true); - expect(instance.between(0, 0, 0), true); - expect(instance.between(-1, 0, 0), true); - expect(instance.between(0, 10, 11), false); - expect(instance.between(0, 10, -1), false); - }); - - test('clamp', () { - final instance = Order.orderInt; - expect(instance.clamp(1, 10, 2), 2); - expect(instance.clamp(1, 10, 10), 10); - expect(instance.clamp(1, 10, 20), 10); - expect(instance.clamp(1, 10, 1), 1); - expect(instance.clamp(1, 10, -10), 1); - }); - - test('orderDate', () { - final prevDate = DateTime(2020); - final currDate = DateTime(2021); - final compareNegative = Order.orderDate.compare(prevDate, currDate); - final comparePositive = Order.orderDate.compare(currDate, prevDate); - final compareSame = Order.orderDate.compare(currDate, currDate); - expect(compareNegative, -1); - expect(comparePositive, 1); - expect(compareSame, 0); - }); - - test('orderNum', () { - final ord = Order.orderNum; - expect(ord.eqv(10, 10), true); - expect(ord.eqv(10.0, 10), true); - expect(ord.gt(10, 0), true); - expect(ord.gt(0, 10), false); - expect(ord.lt(0, 10), true); - }); - - test('orderDouble', () { - final ord = Order.orderDouble; - expect(ord.eqv(10.5, 10.5), true); - expect(ord.eqv(10.0, 10), true); - expect(ord.gt(1.5, 1.2), true); - expect(ord.gt(1.001, 1.005), false); - expect(ord.lt(0.5, 1.2), true); - }); - - test('orderInt', () { - final ord = Order.orderInt; - expect(ord.eqv(10, 10), true); - expect(ord.eqv(-10, 10), false); - expect(ord.gt(1, 1), false); - expect(ord.gt(10, 1), true); - expect(ord.lt(-2, 2), true); - }); - - group('contramap', () { - test('int', () { - final orderParentInt = Order.orderInt.contramap<_Parent>( - (p) => p.value1, - ); - - expect( - orderParentInt.eqv( - _Parent(1, 2.5), - _Parent(1, 12.5), - ), - true, - ); - expect( - orderParentInt.eqv( - _Parent(1, 2.5), - _Parent(4, 2.5), - ), - false, - ); - expect( - orderParentInt.eqv( - _Parent(-1, 2.5), - _Parent(1, 12.5), - ), - false, - ); - }); - - test('double', () { - final orderParentDouble = Order.orderDouble.contramap<_Parent>( - (p) => p.value2, - ); - - expect( - orderParentDouble.eqv( - _Parent(1, 2.5), - _Parent(1, 2.5), - ), - true, - ); - expect( - orderParentDouble.eqv( - _Parent(1, 2.5), - _Parent(1, 12.5), - ), - false, - ); - expect( - orderParentDouble.eqv( - _Parent(-1, 2.5), - _Parent(1, 2), - ), - false, - ); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/partial_order_test.dart b/packages/fpdart/test/src/partial_order_test.dart deleted file mode 100644 index aef2052..0000000 --- a/packages/fpdart/test/src/partial_order_test.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('PartialOrder', () { - group('is a', () { - final instance = PartialOrder.from((a1, a2) => 1); - - test('Eq', () { - expect(instance, isA<Eq>()); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/reader_task_either_test.dart b/packages/fpdart/test/src/reader_task_either_test.dart deleted file mode 100644 index b2f9569..0000000 --- a/packages/fpdart/test/src/reader_task_either_test.dart +++ /dev/null @@ -1,943 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import './utils/utils.dart'; - -void main() { - group('ReaderTaskEither', () { - test('ask', () async { - final apply = ReaderTaskEither.ask<double, int>(); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 12.2); - }); - }); - - test('asks', () async { - final apply = ReaderTaskEither<double, String, int>.asks( - (env) => env.toInt(), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 12); - }); - }); - - group('getOrElse', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).getOrElse( - (left) => left.length, - ); - - final result = await apply.run(12.2); - expect(result, 12); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left(env.toString()), - ).getOrElse( - (left) => left.length, - ); - - final result = await apply.run(12.2); - expect(result, 4); - }); - }); - - group('match', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).match( - (left) => left.length, - (right) => right + 10, - ); - - final result = await apply.run(12.2); - expect(result, 22); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left(env.toString()), - ).match( - (left) => left.length, - (right) => right + 10, - ); - - final result = await apply.run(12.2); - expect(result, 4); - }); - }); - - group('tryCatch', () { - test('Success', () async { - final apply = ReaderTaskEither<double, String, int>.tryCatch( - (env) => Future.value(env.toInt()), - (_, __) => 'error', - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 12); - }); - }); - - test('Failure', () async { - final apply = ReaderTaskEither<double, String, int>.tryCatch( - (env) => Future.error(env.toInt()), - (_, __) => 'error', - ); - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "error"); - }); - }); - - test('throws Exception', () async { - final apply = ReaderTaskEither<double, String, int>.tryCatch((_) { - throw UnimplementedError(); - }, (error, _) { - expect(error, isA<UnimplementedError>()); - return 'error'; - }); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "error"); - }); - }); - }); - - group('flatMap', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).flatMap( - (r) => ReaderTaskEither<double, String, int>( - (env) async => Either.of(r + env.toInt()), - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 24); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left("$env"), - ).flatMap( - (r) => ReaderTaskEither<double, String, int>( - (env) async => Either.of(r + env.toInt()), - ), - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2"); - }); - }); - }); - - group('ap', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).ap<double>( - ReaderTaskEither( - (env) async => Either.of((c) => c / 2), - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 6); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left("$env"), - ).ap<double>( - ReaderTaskEither( - (env) async => Either.of((c) => c / 2), - ), - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2"); - }); - }); - }); - - group('map', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).map((r) => r / 2); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 6); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left('$env'), - ).map((r) => r / 2); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2"); - }); - }); - }); - - group('mapLeft', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).mapLeft( - (l) => '$l and more', - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 12); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left("$env"), - ).mapLeft( - (l) => '$l and more', - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2 and more"); - }); - }); - }); - - group('bimap', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).bimap( - (l) => '$l and more', - (a) => a * 2, - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 24); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left('$env'), - ).bimap( - (l) => '$l and more', - (a) => a * 2, - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2 and more"); - }); - }); - }); - - group('map2', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).map2<int, double>( - ReaderTaskEither( - (env) async => Either.of(env.toInt()), - ), - (b, c) => b / c, - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 1); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left('$env'), - ).map2<int, double>( - ReaderTaskEither( - (env) async => Either.of(env.toInt()), - ), - (b, c) => b / c, - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2"); - }); - }); - }); - - group('map3', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).map3<int, int, double>( - ReaderTaskEither( - (env) async => Either.of(env.toInt()), - ), - ReaderTaskEither( - (env) async => Either.of(env.toInt()), - ), - (b, c, d) => b * c / d, - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 12); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left('$env'), - ).map3<int, int, double>( - ReaderTaskEither( - (env) async => Either.of(env.toInt()), - ), - ReaderTaskEither( - (env) async => Either.of(env.toInt()), - ), - (b, c, d) => b * c / d, - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2"); - }); - }); - }); - - group('andThen', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).andThen( - () => ReaderTaskEither( - (env) async => Either.of( - env.toInt() / 2, - ), - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 6); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left('$env'), - ).andThen( - () => ReaderTaskEither( - (env) async => Either.of(env.toInt() / 2), - ), - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2"); - }); - }); - }); - - group('call', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - )( - ReaderTaskEither( - (env) async => Either.of(env.toInt() / 2), - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 6); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left('$env'), - )( - ReaderTaskEither( - (env) async => Either.of(env.toInt() / 2), - ), - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2"); - }); - }); - }); - - group('filterOrElse', () { - test('Right (true)', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).filterOrElse( - (r) => r > 5, - (r) => 'abc', - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 12); - }); - }); - - test('Right (false)', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).filterOrElse( - (r) => r < 5, - (r) => '$r', - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12"); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left("$env"), - ).filterOrElse( - (r) => r > 5, - (r) => 'none', - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2"); - }); - }); - }); - - test('pure', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left("$env"), - ).pure('abc'); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, "abc"); - }); - }); - - test('run', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ); - - final future = apply.run(12.2); - expect(future, isA<Future>()); - final result = await future; - result.matchTestRight((r) { - expect(r, 12); - }); - }); - - group('fromEither', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>.fromEither( - Either.of(10), - ); - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>.fromEither( - Either.left('error'), - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "error"); - }); - }); - }); - - group('fromOption', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>.fromOption( - Option.of(10), - () => 'none', - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>.fromOption( - Option.none(), - () => 'none', - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "none"); - }); - }); - }); - - group('fromNullable', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>.fromNullable( - 10, - () => "Error", - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>.fromNullable( - null, - () => "error", - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "error"); - }); - }); - }); - - group('fromNullableAsync', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>.fromNullableAsync( - 10, - Task( - () async => "Error", - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>.fromNullableAsync( - null, - Task( - () async => "error", - ), - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "error"); - }); - }); - }); - - test('fromTask', () async { - final apply = ReaderTaskEither<double, String, int>.fromTask( - Task( - () async => 10, - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - group('fromIOOption', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>.fromIOOption( - IOOption.of(10), - () => "none", - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>.fromIOOption( - IOOption.none(), - () => "none", - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "none"); - }); - }); - }); - - group('fromTaskOption', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>.fromTaskOption( - TaskOption.of(10), - () => "none", - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>.fromTaskOption( - TaskOption.none(), - () => "none", - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "none"); - }); - }); - }); - - test('fromTaskEither', () async { - final apply = ReaderTaskEither<double, String, int>.fromTaskEither( - TaskEither.of(10), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('fromIO', () async { - final apply = ReaderTaskEither<double, String, int>.fromIO( - IO( - () => 10, - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('fromIOEither', () async { - final apply = ReaderTaskEither<double, String, int>.fromIOEither( - IOEither.of(10), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('fromReader', () async { - final apply = ReaderTaskEither<double, String, int>.fromReader( - Reader((env) => env.toInt()), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 12); - }); - }); - - test('leftReader', () async { - final apply = ReaderTaskEither<double, String, int>.leftReader( - Reader((env) => "$env"), - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2"); - }); - }); - - test('left', () async { - final apply = ReaderTaskEither<double, String, int>.left( - 'none', - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, 'none'); - }); - }); - - test('leftTask', () async { - final apply = ReaderTaskEither<double, String, int>.leftTask( - Task( - () async => 'none', - ), - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, 'none'); - }); - }); - - group('orElse', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).orElse<int>( - (l) => ReaderTaskEither( - (env) async => Right(l.length), - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 12); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left('$env'), - ).orElse<int>( - (l) => ReaderTaskEither( - (env) async => Right(l.length), - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 4); - }); - }); - }); - - group('alt', () { - test('Right', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).alt( - () => ReaderTaskEither( - (env) async => Either.of(env.toInt() * 2), - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 12); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.left('none'), - ).alt( - () => ReaderTaskEither( - (env) async => Either.of(env.toInt() * 12), - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 144); - }); - }); - }); - - test('swap', () async { - final apply = ReaderTaskEither<double, String, int>( - (env) async => Either.of(env.toInt()), - ).swap(); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, 12); - }); - }); - - test('of', () async { - final apply = ReaderTaskEither<double, String, int>.of(10); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('flatten', () async { - final apply = ReaderTaskEither<double, String, - ReaderTaskEither<double, String, int>>.of( - ReaderTaskEither<double, String, int>.of(10), - ); - - final result = await ReaderTaskEither.flatten(apply).run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('chainFirst', () async { - final apply = ReaderTaskEither<double, String, int>.of(10); - var sideEffect = 10; - - final chain = apply.chainFirst((b) { - sideEffect = 100; - return ReaderTaskEither.left("$b"); - }); - - expect(sideEffect, 10); - final result = await chain.run(12.2); - result.matchTestLeft((l) { - expect(l, "10"); - expect(sideEffect, 100); - }); - }); - - group('Do Notation', () { - test('should return the correct value', () async { - final doTaskEither = ReaderTaskEither<double, String, int>.Do( - (_) => _( - ReaderTaskEither.asks((env) => env.toInt()), - ), - ); - - final run = await doTaskEither.run(12.2); - run.matchTestRight((r) { - expect(r, 12); - }); - }); - - test('should extract the correct values', () async { - final doTaskEither = - ReaderTaskEither<double, String, int>.Do((_) async { - final a = await _(ReaderTaskEither.of(10)); - final b = await _(ReaderTaskEither.asks((env) => env.toInt())); - return a + b; - }); - - final run = await doTaskEither.run(12.2); - run.matchTestRight((r) { - expect(r, 22); - }); - }); - - test('should return Left if any Either is Left', () async { - final doTaskEither = - ReaderTaskEither<double, String, int>.Do((_) async { - final a = await _(ReaderTaskEither.of(10)); - final b = await _(ReaderTaskEither.asks((env) => env.toInt())); - final c = await _( - ReaderTaskEither<double, String, int>.left('error'), - ); - - return a + b + c; - }); - - final run = await doTaskEither.run(12.2); - run.matchTestLeft((l) { - expect(l, 'error'); - }); - }); - - test('should rethrow if throw is used inside Do', () { - final doTaskEither = ReaderTaskEither<double, String, int>.Do((_) { - _(ReaderTaskEither.of(10)); - throw UnimplementedError(); - }); - - expect( - () => doTaskEither.run(12.2), - throwsA( - const TypeMatcher<UnimplementedError>(), - ), - ); - }); - - test('should rethrow if Left is thrown inside Do', () { - final doTaskEither = ReaderTaskEither<double, String, int>.Do((_) { - _( - ReaderTaskEither.of(10), - ); - throw Left('error'); - }); - - expect( - () => doTaskEither.run(12.2), - throwsA( - const TypeMatcher<Left>(), - ), - ); - }); - - test('should no execute past the first Left', () async { - var mutable = 10; - - final doTaskEitherLeft = - ReaderTaskEither<double, String, int>.Do((_) async { - final a = await _(ReaderTaskEither.of(10)); - final b = - await _(ReaderTaskEither<double, String, int>.left("error")); - mutable += 10; - return a + b; - }); - - final runLeft = await doTaskEitherLeft.run(12.2); - expect(mutable, 10); - runLeft.matchTestLeft((l) { - expect(l, "error"); - }); - - final doTaskEitherRight = - ReaderTaskEither<double, String, int>.Do((_) async { - final a = await _(ReaderTaskEither.asks((env) => env.toInt())); - mutable += 10; - return a; - }); - - final runRight = await doTaskEitherRight.run(12.2); - expect(mutable, 20); - runRight.matchTestRight((r) { - expect(r, 12); - }); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/reader_task_test.dart b/packages/fpdart/test/src/reader_task_test.dart deleted file mode 100644 index 0ae274f..0000000 --- a/packages/fpdart/test/src/reader_task_test.dart +++ /dev/null @@ -1,228 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import 'utils/utils.dart'; - -void main() { - group('ReaderTask', () { - test('ask', () async { - final apply = ReaderTask.ask<String, int>(); - - final result = await apply.run("abc"); - expect(result, "abc"); - }); - - test('asks', () async { - final apply = ReaderTask<String, int>.asks( - (env) => env.length, - ); - - final result = await apply.run("abc"); - expect(result, 3); - }); - - group('map', () { - test('int to int', () async { - final apply = ReaderTask<String, int>( - (env) async => env.length, - ).map((a) => a + 1); - - final result = await apply.run("abc"); - expect(result, 4); - }); - }); - - test('ap', () async { - final apply = ReaderTask<String, int>( - (env) async => env.length, - ).ap( - ReaderTask( - (env) async => (a) => a + 1, - ), - ); - - final result = await apply.run("abc"); - expect(result, 4); - }); - - test('flatMap', () async { - final apply = ReaderTask<String, int>( - (env) async => env.length, - ) - .flatMap( - (a) => ReaderTask( - (env) async => '$a-$env', - ), - ) - .flatMap( - (a) => ReaderTask( - (env) async => a.length + env.length, - ), - ); - - final result = await apply.run("abc"); - expect(result, 8); - }); - - test('pure', () async { - final apply = ReaderTask<String, int>( - (env) async => env.length, - ).pure('abc'); - - final result = await apply.run("abc"); - expect(result, 'abc'); - }); - - test('map2', () async { - final apply = ReaderTask<String, int>( - (env) async => env.length, - ).map2<String, int>( - ReaderTask.of('abc'), - (a, c) => a + c.length, - ); - - final result = await apply.run("abc"); - expect(result, 6); - }); - - test('map3', () async { - final apply = ReaderTask<String, int>( - (env) async => env.length, - ).map3<int, String, double>( - ReaderTask.of(2), - ReaderTask.of('abc'), - (a, c, d) => (a + d.length) / c, - ); - - final result = await apply.run("abc"); - expect(result, 3); - }); - - test('of', () async { - final apply = ReaderTask<String, int>.of(10); - - final result = await apply.run("abc"); - expect(result, 10); - }); - - test('run', () async { - final apply = ReaderTask<String, int>.of(10); - final future = apply.run("abc"); - - expect(future, isA<Future<int>>()); - final result = await future; - expect(result, 10); - }); - - test('flatten', () async { - final apply = ReaderTask<String, ReaderTask<String, int>>.of( - ReaderTask<String, int>.of(10), - ); - - final mid = await apply.run("abc"); - final flatten = ReaderTask.flatten(apply); - - final resultMid = await mid.run("abc"); - final resultFlatten = await flatten.run("abc"); - - expect(resultMid, 10); - expect(resultFlatten, 10); - expect(resultMid, resultFlatten); - }); - - group('andThen', () { - test('run a Task after another Task', () async { - final apply = ReaderTask<String, int>( - (env) async => env.length, - ).andThen( - () => ReaderTask( - (env) async => env.length * 2, - ), - ); - - final result = await apply.run("abc"); - expect(result, 6); - }); - - test('never run the second Task since the first throws', () async { - final apply = ReaderTask<String, int>( - (env) async => throw UnimplementedError(), - ); - - final result = apply.andThen<double>( - () => ReaderTask.of(12.2), - ); - expect(() => result.run("abc"), - throwsA(const TypeMatcher<UnimplementedError>())); - }); - }); - - group('call', () { - test('run a Task after another Task', () async { - final apply = ReaderTask<String, int>( - (env) async => env.length, - )(ReaderTask.of('abc')); - - final result = await apply.run("abc"); - expect(result, 'abc'); - }); - - test('run the second Task but return throw error', () async { - final apply = - ReaderTask<String, int>((env) async => throw UnimplementedError())( - ReaderTask.of('abc'), - ); - - expect(() => apply.run("abc"), - throwsA(const TypeMatcher<UnimplementedError>())); - }); - }); - - test('toReaderTaskEither', () async { - final apply = ReaderTask<String, int>.of(10).toReaderTaskEither<double>(); - - final result = await apply.run("abc"); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - group('Do Notation', () { - test('should return the correct value', () async { - final doTask = ReaderTask<String, int>.Do( - (_) => _( - ReaderTask.of(10), - ), - ); - - final run = await doTask.run("abc"); - expect(run, 10); - }); - - test('should extract the correct values', () async { - final doTask = ReaderTask<String, int>.Do((_) async { - final a = await _(ReaderTask((env) async => env.length)); - final b = await _(ReaderTask((env) async => env.length)); - return a + b; - }); - - final run = await doTask.run("abc"); - expect(run, 6); - }); - - test('should not execute until run is called', () async { - var mutable = 10; - final doTask = ReaderTask<String, int>.Do((_) async { - final a = await _(ReaderTask.of(10)); - final b = await _(ReaderTask.of(5)); - mutable += 10; - return a + b; - }); - - expect(mutable, 10); - final run = await doTask.run("abc"); - expect(mutable, 20); - expect(run, 15); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/reader_test.dart b/packages/fpdart/test/src/reader_test.dart deleted file mode 100644 index 20730b7..0000000 --- a/packages/fpdart/test/src/reader_test.dart +++ /dev/null @@ -1,134 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('Reader', () { - group('is a', () { - final reader = Reader<String, int>((r) => r.length); - - test('Monad', () { - expect(reader, isA<Monad2>()); - }); - - test('Applicative', () { - expect(reader, isA<Applicative2>()); - }); - - test('Functor', () { - expect(reader, isA<Functor2>()); - }); - }); - - test('map', () { - final reader = Reader<String, int>((r) => r.length); - final ap = reader.map((a) => a + 1); - final result = ap.run('abc'); - expect(result, 4); - }); - - test('map2', () { - final reader = Reader<String, int>((r) => r.length); - final reader1 = Reader<String, int>((r) => r.length * 2); - final ap = reader.map2<int, int>(reader1, (a, c) => a * c); - final result = ap.run('abc'); - expect(result, 18); - }); - - test('map3', () { - final reader = Reader<String, int>((r) => r.length); - final reader1 = Reader<String, int>((r) => r.length * 2); - final reader2 = Reader<String, int>((r) => r.length * 3); - final ap = - reader.map3<int, int, int>(reader1, reader2, (a, c, d) => a * c * d); - final result = ap.run('ab'); - expect(result, 48); - }); - - test('ap', () { - final reader = Reader<String, int>((r) => r.length); - final ap = - reader.ap<double>(Reader((a) => (int n) => (n + a.length) / 2)); - final result = ap.run('abc'); - expect(result, 3.0); - }); - - test('flatMap', () { - final reader = Reader<String, int>((r) => r.length); - final ap = - reader.flatMap((a) => Reader<String, int>((b) => a + b.length)); - final result = ap.run('abc'); - expect(result, 6); - }); - - test('pure', () { - final reader = Reader<String, int>((r) => r.length); - final ap = reader.pure(10); - final result = ap.run('abc'); - expect(result, 10); - }); - - test('andThen', () { - final reader = Reader<String, int>((r) => r.length); - final ap = - reader.andThen(() => Reader<String, double>((r) => r.length / 2)); - final result = ap.run('abc'); - expect(result, 1.5); - }); - - test('call', () { - final reader = Reader<String, int>((r) => r.length); - final ap = reader(Reader<String, double>((r) => r.length / 2)); - final result = ap.run('abc'); - expect(result, 1.5); - }); - - test('compose', () { - final reader = Reader<String, int>((r) => r.length); - final ap = reader.compose(Reader<String, double>((r) => r.length / 2)); - final result = ap.run('abc'); - expect(result, 1.5); - }); - - test('local', () { - final reader = Reader<String, int>((r) => r.length); - final ap = reader.local<double>((context) => '$context'); - final result = ap.run(7.5); - expect(result, 3); - }); - - test('ask', () { - final reader = Reader<String, int>((r) => r.length); - final ap = reader.ask(); - final result = ap.run('abc'); - expect(result, 'abc'); - }); - - test('asks', () { - final reader = Reader<String, int>((r) => r.length); - final ap = reader.asks((r) => r.length * 2); - final result = ap.run('abc'); - expect(result, 6); - }); - - test('flatten', () { - final reader = Reader<String, Reader<String, int>>( - (r) => Reader<String, int>((r) => r.length)); - final ap = Reader.flatten(reader); - expect(ap, isA<Reader<String, int>>()); - final result = ap.run('abc'); - expect(result, 3); - }); - }); - - test('chainFirst', () async { - final task = Reader<String, int>(((r) => r.length)); - var sideEffect = 10; - final chain = task.chainFirst((b) { - sideEffect = 100; - return Reader<String, double>((r) => r.length / 2); - }); - final r = await chain.run("abc"); - expect(r, 3); - expect(sideEffect, 100); - }); -} diff --git a/packages/fpdart/test/src/semigroup_test.dart b/packages/fpdart/test/src/semigroup_test.dart deleted file mode 100644 index d8470c4..0000000 --- a/packages/fpdart/test/src/semigroup_test.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('Semigroup', () { - test('.instance', () { - final instance = Semigroup.instance<String>((a1, a2) => '$a1$a2'); - expect(instance.combine('a', 'b'), 'ab'); - expect(instance.combine('a', instance.combine('b', 'c')), - instance.combine(instance.combine('a', 'b'), 'c')); - expect(instance.combineN('a', 3), 'aaa'); - }); - - test('.first', () { - final instance = Semigroup.first<String>(); - expect(instance.combine('a', 'b'), 'a'); - expect(instance.combine('a', instance.combine('b', 'c')), - instance.combine(instance.combine('a', 'b'), 'c')); - expect(instance.combineN('a', 3), 'a'); - }); - - test('.last', () { - final instance = Semigroup.last<String>(); - expect(instance.combine('a', 'b'), 'b'); - expect(instance.combine('a', instance.combine('b', 'c')), - instance.combine(instance.combine('a', 'b'), 'c')); - expect(instance.combineN('a', 3), 'a'); - }); - - test('.combineN', () { - final instance = Semigroup.instance<int>((a1, a2) => a1 + a2); - expect(instance.combineN(1, 10), 10); - }); - - test('.intercalate', () { - final instance = Semigroup.instance<String>((a1, a2) => '$a1$a2'); - final intercalate = instance.intercalate('-'); - expect(intercalate.combine('a', 'b'), 'a-b'); - expect(intercalate.combineN('a', 3), 'a-a-a'); - }); - - test('.reverse', () { - final instance = Semigroup.instance<String>((a1, a2) => '$a1$a2'); - final reverse = instance.reverse(); - expect(reverse.combine('a', 'b'), 'ba'); - expect(reverse.combine('a', 'b'), instance.combine('b', 'a')); - }); - }); -} diff --git a/packages/fpdart/test/src/semilattice_test.dart b/packages/fpdart/test/src/semilattice_test.dart deleted file mode 100644 index f1e73bd..0000000 --- a/packages/fpdart/test/src/semilattice_test.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('Semilattice', () { - group('is a', () { - final instance = Semilattice.instance<int>((a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA<Semigroup>()); - }); - - test('Band', () { - expect(instance, isA<Band>()); - }); - - test('CommutativeSemigroup', () { - expect(instance, isA<CommutativeSemigroup>()); - }); - }); - - test('combineN (from Band)', () { - final instance = Semilattice.instance<int>((a1, a2) => a1 + a2); - expect(instance.combineN(1, 1), 1); - expect(instance.combineN(1, 10), 2); - }); - - test('asMeetPartialOrder', () { - final instance = Semilattice.instance<int>((a1, a2) => a1 + a2); - final eq = Eq.instance<int>((a1, a2) => a1 == a2); - final partialOrder = instance.asMeetPartialOrder(eq); - expect(partialOrder.partialCompare(1, 1), 0); - expect(partialOrder.partialCompare(1, 0), -1); - expect(partialOrder.partialCompare(0, 1), 1); - expect(partialOrder.partialCompare(2, 1), null); - }); - - test('asJoinPartialOrder', () { - final instance = Semilattice.instance<int>((a1, a2) => a1 + a2); - final eq = Eq.instance<int>((a1, a2) => a1 == a2); - final partialOrder = instance.asJoinPartialOrder(eq); - expect(partialOrder.partialCompare(1, 1), 0); - expect(partialOrder.partialCompare(1, 0), 1); - expect(partialOrder.partialCompare(0, 1), -1); - expect(partialOrder.partialCompare(2, 1), null); - }); - }); -} diff --git a/packages/fpdart/test/src/state_async_test.dart b/packages/fpdart/test/src/state_async_test.dart deleted file mode 100644 index da0fb9c..0000000 --- a/packages/fpdart/test/src/state_async_test.dart +++ /dev/null @@ -1,214 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('StateAsync', () { - group('is a', () { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - - test('Monad', () { - expect(state, isA<Monad2>()); - }); - - test('Applicative', () { - expect(state, isA<Applicative2>()); - }); - - test('Functor', () { - expect(state, isA<Functor2>()); - }); - }); - - test('map', () async { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - final ap = state.map((a) => a + 1); - final result = await ap.run('aaa'); - expect(result.$1, 4); - expect(result.$2, 'aaaa'); - }); - - test('map2', () async { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - final state1 = StateAsync<String, double>( - (s) async => (s.length / 2, '${s}b'), - ); - final ap = state.map2<double, double>(state1, (a, c) => c * a); - final result = await ap.run('aaa'); - expect(result.$1, 6); - expect(result.$2, 'aaaab'); - }); - - test('map3', () async { - final state = StateAsync<String, int>( - (s) async => (s.length, '${s}a'), - ); - final state1 = StateAsync<String, double>( - (s) async => (s.length / 2, '${s}b'), - ); - final state2 = StateAsync<String, String>( - (s) async => ('${s}aaa', '${s}b'), - ); - final ap = state.map3<double, String, double>( - state1, - state2, - (a, c, d) => d.length + (c * a), - ); - final result = await ap.run('aaa'); - expect(result.$1, 14); - expect(result.$2, 'aaaabb'); - }); - - test('ap', () async { - final state = StateAsync<String, int>( - (s) async => (s.length, '${s}a'), - ); - final ap = state.ap<String>( - StateAsync( - (s) async => ((int n) => '$n$s', s), - ), - ); - final result = await ap.run('aaa'); - expect(result.$1, '3aaa'); - expect(result.$2, 'aaaa'); - }); - - test('andThen', () async { - final state = StateAsync<String, int>( - (s) async => (s.length, '${s}a'), - ); - final ap = state.andThen( - () => StateAsync<String, double>( - (s) async => (s.length / 2, '${s}a'), - ), - ); - final result = await ap.run('aaa'); - expect(result.$1, 2); - expect(result.$2, 'aaaaa'); - }); - - test('call', () async { - final state = StateAsync<String, int>( - (s) async => (s.length, '${s}a'), - ); - final ap = state( - StateAsync<String, double>( - (s) async => (s.length / 2, '${s}a'), - ), - ); - final result = await ap.run('aaa'); - expect(result.$1, 2); - expect(result.$2, 'aaaaa'); - }); - - test('fromState', () async { - final state = - StateAsync<String, int>.fromState(State((s) => (s.length, '${s}a'))); - final result = await state.run('aaa'); - expect(result.$1, 3); - expect(result.$2, 'aaaa'); - }); - - test('pure', () async { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - final ap = state.pure(10); - final result = await ap.run('aaa'); - expect(result.$1, 10); - expect(result.$2, 'aaa'); - }); - - test('flatMap', () async { - final state = - StateAsync<List<int>, int>((s) async => (s.first, s.sublist(1))); - final ap = state.flatMap<double>( - (a) => StateAsync( - (s) async => (a / 2, s.sublist(1)), - ), - ); - final result = await ap.run([1, 2, 3, 4, 5]); - expect(result.$1, 0.5); - expect(result.$2, [3, 4, 5]); - }); - - test('get', () async { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - final ap = state.get(); - final result = await ap.run('aaa'); - expect(result.$1, 'aaa'); - expect(result.$2, 'aaa'); - }); - - test('gets', () async { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - final ap = state.gets((s) => s.length * 2); - final result = await ap.run('aaa'); - expect(result.$1, 6); - expect(result.$2, 'aaa'); - }); - - test('modify', () async { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - final ap = state.modify((state) => 'b$state'); - final result = await ap.run('aaa'); - expect(result.$1, unit); - expect(result.$2, 'baaa'); - }); - - test('put', () async { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - final ap = state.put('b'); - final result = await ap.run('aaa'); - expect(result.$2, 'b'); - }); - - test('evaluate', () async { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - final result = await state.evaluate('aaa'); - expect(result, 3); - }); - - test('execute', () async { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - final result = await state.execute('aaa'); - expect(result, 'aaaa'); - }); - - test('run', () async { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - final result = await state.run('aaa'); - expect(result, isA<Record>()); - expect(result.$1, 3); - expect(result.$2, 'aaaa'); - }); - - test('flatten', () async { - final state = StateAsync<String, StateAsync<String, int>>( - (s) async => ( - StateAsync<String, int>( - (s) async => (s.length, '${s}a'), - ), - '${s}a', - ), - ); - final ap = StateAsync.flatten(state); - expect(ap, isA<StateAsync<String, int>>()); - final result = await ap.run('aaa'); - expect(result.$1, 4); - expect(result.$2, 'aaaaa'); - }); - }); - - test('chainFirst', () async { - final state = StateAsync<String, int>((s) async => (s.length, '${s}a')); - var sideEffect = 10; - final chain = state.chainFirst((b) { - sideEffect = 100; - return StateAsync<String, double>((s) async => (s.length / 2, 'z${s}')); - }); - final result = await chain.run('abc'); - expect(result.$1, 3); - - // It changes the value of `second`! - expect(result.$2, 'zabca'); - expect(sideEffect, 100); - }); -} diff --git a/packages/fpdart/test/src/state_test.dart b/packages/fpdart/test/src/state_test.dart deleted file mode 100644 index 9be0f84..0000000 --- a/packages/fpdart/test/src/state_test.dart +++ /dev/null @@ -1,266 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -void main() { - group('State', () { - group('is a', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - - test('Monad', () { - expect(state, isA<Monad2>()); - }); - - test('Applicative', () { - expect(state, isA<Applicative2>()); - }); - - test('Functor', () { - expect(state, isA<Functor2>()); - }); - }); - - test('map', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - final ap = state.map((a) => a + 1); - final result = ap.run('aaa'); - expect(result.$1, 4); - expect(result.$2, 'aaaa'); - }); - - test('map2', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - final state1 = State<String, double>( - (s) => (s.length / 2, '${s}b'), - ); - final ap = state.map2<double, double>(state1, (a, c) => c * a); - final result = ap.run('aaa'); - expect(result.$1, 6); - expect(result.$2, 'aaaab'); - }); - - test('map3', () { - final state = State<String, int>( - (s) => (s.length, '${s}a'), - ); - final state1 = State<String, double>( - (s) => (s.length / 2, '${s}b'), - ); - final state2 = State<String, String>( - (s) => ('${s}aaa', '${s}b'), - ); - final ap = state.map3<double, String, double>( - state1, - state2, - (a, c, d) => d.length + (c * a), - ); - final result = ap.run('aaa'); - expect(result.$1, 14); - expect(result.$2, 'aaaabb'); - }); - - test('ap', () { - final state = State<String, int>( - (s) => (s.length, '${s}a'), - ); - final ap = state.ap<String>( - State( - (s) => ((int n) => '$n$s', s), - ), - ); - final result = ap.run('aaa'); - expect(result.$1, '3aaa'); - expect(result.$2, 'aaaa'); - }); - - test('andThen', () { - final state = State<String, int>( - (s) => (s.length, '${s}a'), - ); - final ap = state.andThen( - () => State<String, double>( - (s) => (s.length / 2, '${s}a'), - ), - ); - final result = ap.run('aaa'); - expect(result.$1, 2); - expect(result.$2, 'aaaaa'); - }); - - test('call', () { - final state = State<String, int>( - (s) => (s.length, '${s}a'), - ); - final ap = state( - State<String, double>( - (s) => (s.length / 2, '${s}a'), - ), - ); - final result = ap.run('aaa'); - expect(result.$1, 2); - expect(result.$2, 'aaaaa'); - }); - - test('toStateAsync', () async { - final state = State<String, int>((s) => (s.length, '${s}a')); - final ap = state.toStateAsync(); - final result = await ap.run('aaa'); - expect(result.$1, 3); - expect(result.$2, 'aaaa'); - }); - - test('pure', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - final ap = state.pure(10); - final result = ap.run('aaa'); - expect(result.$1, 10); - expect(result.$2, 'aaa'); - }); - - test('flatMap', () { - final state = State<List<int>, int>((s) => (s.first, s.sublist(1))); - final ap = state.flatMap<double>( - (a) => State( - (s) => (a / 2, s.sublist(1)), - ), - ); - final result = ap.run([1, 2, 3, 4, 5]); - expect(result.$1, 0.5); - expect(result.$2, [3, 4, 5]); - }); - - test('get', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - final ap = state.get(); - final result = ap.run('aaa'); - expect(result.$1, 'aaa'); - expect(result.$2, 'aaa'); - }); - - test('gets', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - final ap = state.gets((s) => s.length * 2); - final result = ap.run('aaa'); - expect(result.$1, 6); - expect(result.$2, 'aaa'); - }); - - test('modify', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - final ap = state.modify((state) => 'b$state'); - final result = ap.run('aaa'); - expect(result.$1, unit); - expect(result.$2, 'baaa'); - }); - - test('put', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - final ap = state.put('b'); - final result = ap.run('aaa'); - expect(result.$2, 'b'); - }); - - test('evaluate', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - final result = state.evaluate('aaa'); - expect(result, 3); - }); - - test('execute', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - final result = state.execute('aaa'); - expect(result, 'aaaa'); - }); - - test('run', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - final result = state.run('aaa'); - expect(result, isA<Record>()); - expect(result.$1, 3); - expect(result.$2, 'aaaa'); - }); - - test('flatten', () { - final state = State<String, State<String, int>>( - (s) => ( - State<String, int>( - (s) => (s.length, '${s}a'), - ), - '${s}a', - ), - ); - final ap = State.flatten(state); - expect(ap, isA<State<String, int>>()); - final result = ap.run('aaa'); - expect(result.$1, 4); - expect(result.$2, 'aaaaa'); - }); - }); - - test('chainFirst', () { - final state = State<String, int>((s) => (s.length, '${s}a')); - var sideEffect = 10; - final chain = state.chainFirst((b) { - sideEffect = 100; - return State<String, double>((s) => (s.length / 2, 'z${s}')); - }); - final result = chain.run('abc'); - expect(result.$1, 3); - - // It changes the value of `second`! - expect(result.$2, 'zabca'); - expect(sideEffect, 100); - }); - - test('traverseListWithIndex', () { - var sideEffect = 0; - final list = ['a', 'b']; - - final traverse = State.traverseListWithIndex( - list, - (a, i) => State<int, String>((s) { - sideEffect++; - return (a + i.toString(), s); - })); - expect(sideEffect, 0); - final (resultList, resultState) = traverse.run(1); - expect(resultList, ['a0', 'b1']); - expect(resultState, 1); - expect(sideEffect, list.length); - }); - - test('traverseList', () { - var sideEffect = 0; - final list = ['a', 'b']; - final traverse = State.traverseList( - list, - (a) => State<int, String>((s) { - sideEffect++; - return (a, s); - })); - expect(sideEffect, 0); - final (resultList, resultState) = traverse.run(1); - expect(resultList, ['a', 'b']); - expect(resultState, 1); - expect(sideEffect, list.length); - }); - - test('sequenceList', () { - var sideEffect = 0; - final list = [ - State<int, String>((s) { - sideEffect++; - return ('a', s); - }), - State<int, String>((s) { - sideEffect++; - return ('b', s); - }) - ]; - final sequence = State.sequenceList(list); - expect(sideEffect, 0); - final (resultList, resultState) = sequence.run(1); - expect(resultList, ['a', 'b']); - expect(resultState, 1); - expect(sideEffect, list.length); - }); -} diff --git a/packages/fpdart/test/src/task_either_test.dart b/packages/fpdart/test/src/task_either_test.dart deleted file mode 100644 index 2e45e7d..0000000 --- a/packages/fpdart/test/src/task_either_test.dart +++ /dev/null @@ -1,1027 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import './utils/utils.dart'; - -void main() { - group('TaskEither', () { - group('tryCatch', () { - test('Success', () async { - final task = TaskEither<String, int>.tryCatch( - () => Future.value(10), (_, __) => 'error'); - final r = await task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Failure', () async { - final task = TaskEither<String, int>.tryCatch( - () => Future.error(10), (_, __) => 'error'); - final r = await task.run(); - r.match((l) => expect(l, 'error'), (_) { - fail('should be left'); - }); - }); - - test('throws Exception', () async { - final task = TaskEither<String, int>.tryCatch(() { - throw UnimplementedError(); - }, (error, _) { - expect(error, isA<UnimplementedError>()); - return 'error'; - }); - final r = await task.run(); - r.match((l) => expect(l, 'error'), (_) { - fail('should be left'); - }); - }); - }); - - group('tryCatchK', () { - test('Success', () async { - final task = TaskEither<String, int>.right(10); - final ap = task.flatMap(TaskEither.tryCatchK( - (n) => Future.value(n + 5), - (_, __) => 'error', - )); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 15)); - }); - - test('Failure', () async { - final task = TaskEither<String, int>.right(10); - final ap = task.flatMap(TaskEither.tryCatchK( - (n) => Future<int>.error(n + 5), - (_, __) => 'error', - )); - final r = await ap.run(); - r.match((l) => expect(l, 'error'), (_) { - fail('should be left'); - }); - }); - - test('throws Exception', () async { - final task = TaskEither<String, int>.right(10); - final ap = task.flatMap(TaskEither.tryCatchK<String, int, int>((_) { - throw UnimplementedError(); - }, (error, _) { - expect(error, isA<UnimplementedError>()); - return 'error'; - })); - final r = await ap.run(); - r.match((l) => expect(l, 'error'), (_) { - fail('should be left'); - }); - }); - }); - - group('flatMap', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = task.flatMap( - (r) => TaskEither<String, int>(() async => Either.of(r + 10))); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 20)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = task.flatMap( - (r) => TaskEither<String, int>(() async => Either.of(r + 10))); - final r = await ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('chainEither', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = task.chainEither((r) => Either.of(r + 10)); - final r = await ap.run(); - r.matchTestRight((r) => expect(r, 20)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = task.chainEither((r) => Either.of(r + 10)); - final r = await ap.run(); - r.matchTestLeft((l) => expect(l, 'abc')); - }); - }); - - group('bindEither', () { - test('Right', () async { - final task = TaskEither<String, int>(() 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<String, int>(() 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<String, int>(() async => Either.of(10)); - final ap = task - .ap<double>(TaskEither(() async => Either.of((int c) => c / 2))); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 5.0)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = task - .ap<double>(TaskEither(() async => Either.of((int c) => c / 2))); - final r = await ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('map', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = task.map((r) => r / 2); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 5.0)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = task.map((r) => r / 2); - final r = await ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('mapLeft', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = task.mapLeft((l) => '$l and more'); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = task.mapLeft((l) => '$l and more'); - final r = await ap.run(); - r.match((l) => expect(l, 'abc and more'), (_) { - fail('should be left'); - }); - }); - }); - - group('bimap', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = task.bimap((l) => '$l and more', (a) => a * 2); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 20)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = task.bimap((l) => '$l and more', (a) => a * 2); - final r = await ap.run(); - r.match((l) => expect(l, 'abc and more'), (_) { - fail('should be left'); - }); - }); - }); - - group('map2', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = task.map2<int, double>( - TaskEither<String, int>(() async => Either.of(2)), (b, c) => b / c); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 5.0)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = task.map2<int, double>( - TaskEither<String, int>(() async => Either.of(2)), (b, c) => b / c); - final r = await ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('map3', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = task.map3<int, int, double>( - TaskEither<String, int>(() async => Either.of(2)), - TaskEither<String, int>(() async => Either.of(5)), - (b, c, d) => b * c / d); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 4.0)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = task.map3<int, int, double>( - TaskEither<String, int>(() async => Either.of(2)), - TaskEither<String, int>(() async => Either.of(5)), - (b, c, d) => b * c / d); - final r = await ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('andThen', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = task.andThen( - () => TaskEither<String, double>(() async => Either.of(12.5))); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 12.5)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = task.andThen( - () => TaskEither<String, double>(() async => Either.of(12.5))); - final r = await ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - group('call', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = - task(TaskEither<String, double>(() async => Either.of(12.5))); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 12.5)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = - task(TaskEither<String, double>(() async => Either.of(12.5))); - final r = await ap.run(); - r.match((r) { - expect(r, 'abc'); - }, (_) { - fail('should be left'); - }); - }); - }); - - group('filterOrElse', () { - test('Right (true)', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = task.filterOrElse((r) => r > 5, (r) => 'abc'); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Right (false)', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = task.filterOrElse((r) => r < 5, (r) => 'none'); - final r = await ap.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = task.filterOrElse((r) => r > 5, (r) => 'none'); - final r = await ap.run(); - r.match((l) => expect(l, 'abc'), (_) { - fail('should be left'); - }); - }); - }); - - test('pure', () async { - final task = TaskEither<String, int>(() async => Either.left('abc')); - final ap = task.pure('abc'); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 'abc')); - }); - - test('run', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final future = task.run(); - expect(future, isA<Future>()); - final r = await future; - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - group('fromEither', () { - test('Right', () async { - final task = TaskEither<String, int>.fromEither(Either.of(10)); - final r = await task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () async { - final task = TaskEither<String, int>.fromEither(Either.left('error')); - final r = await task.run(); - r.match((l) => expect(l, 'error'), (_) { - fail('should be left'); - }); - }); - }); - - group('fromOption', () { - test('Right', () async { - final task = - TaskEither<String, int>.fromOption(Option.of(10), () => 'none'); - final r = await task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () async { - final task = - TaskEither<String, int>.fromOption(Option.none(), () => 'none'); - final r = await task.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('fromNullable', () { - test('Right', () async { - final task = TaskEither<String, int>.fromNullable(10, () => "Error"); - final result = await task.run(); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final task = TaskEither<String, int>.fromNullable(null, () => "Error"); - final result = await task.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('fromNullableAsync', () { - test('Right', () async { - final task = TaskEither<String, int>.fromNullableAsync( - 10, Task(() async => "Error")); - final result = await task.run(); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final task = TaskEither<String, int>.fromNullableAsync( - null, Task(() async => "Error")); - final result = await task.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('fromPredicate', () { - test('True', () async { - final task = TaskEither<String, int>.fromPredicate( - 20, (n) => n > 10, (n) => '$n'); - final r = await task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 20)); - }); - - test('False', () async { - final task = TaskEither<String, int>.fromPredicate( - 10, (n) => n > 10, (n) => '$n'); - final r = await task.run(); - r.match((l) => expect(l, '10'), (_) { - fail('should be left'); - }); - }); - }); - - test('fromTask', () async { - final task = TaskEither<String, int>.fromTask(Task(() async => 10)); - final r = await task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('left', () async { - final task = TaskEither<String, int>.left('none'); - final r = await task.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('right', () async { - final task = TaskEither<String, int>.right(10); - final r = await task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('leftTask', () async { - final task = TaskEither<String, int>.leftTask(Task(() async => 'none')); - final r = await task.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('rightTask', () async { - final task = TaskEither<String, int>.rightTask(Task.of(10)); - final r = await task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - group('match', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ex = task.match((l) => l.length, (r) => r + 10); - final r = await ex.run(); - expect(r, 20); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('none')); - final ex = task.match((l) => l.length, (r) => r + 10); - final r = await ex.run(); - expect(r, 4); - }); - }); - - group('getOrElse', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ex = task.getOrElse((l) => l.length); - final r = await ex.run(); - expect(r, 10); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('none')); - final ex = task.getOrElse((l) => l.length); - final r = await ex.run(); - expect(r, 4); - }); - }); - - group('orElse', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ex = - task.orElse<int>((l) => TaskEither(() async => Right(l.length))); - final r = await ex.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('none')); - final ex = - task.orElse<int>((l) => TaskEither(() async => Right(l.length))); - final r = await ex.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 4)); - }); - }); - - group('alt', () { - test('Right', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ex = task.alt(() => TaskEither(() async => Either.of(20))); - final r = await ex.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () async { - final task = TaskEither<String, int>(() async => Either.left('none')); - final ex = task.alt(() => TaskEither(() async => Either.of(20))); - final r = await ex.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 20)); - }); - }); - - test('swap', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ex = task.swap(); - final r = await ex.run(); - r.match((l) => expect(l, 10), (_) { - fail('should be left'); - }); - }); - - test('of', () async { - final task = TaskEither<String, int>.of(10); - final r = await task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('flatten', () async { - final task = TaskEither<String, TaskEither<String, int>>.of( - TaskEither<String, int>.of(10)); - final ap = TaskEither.flatten(task); - final r = await ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('chainFirst', () async { - final task = TaskEither<String, int>.of(10); - var sideEffect = 10; - final chain = task.chainFirst((b) { - sideEffect = 100; - return TaskEither.left("abc"); - }); - final r = await chain.run(); - r.match( - (l) => fail('should be right'), - (r) { - expect(r, 10); - expect(sideEffect, 100); - }, - ); - }); - - test('delay', () async { - final task = TaskEither<String, int>(() async => Either.of(10)); - final ap = task.delay(const Duration(seconds: 2)); - final stopwatch = Stopwatch(); - stopwatch.start(); - await ap.run(); - stopwatch.stop(); - expect(stopwatch.elapsedMilliseconds >= 2000, true); - }); - - group('sequenceList', () { - test('Right', () async { - var sideEffect = 0; - final list = [ - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right<String, int>(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right<String, int>(2); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right<String, int>(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right<String, int>(4); - }), - ]; - final traverse = TaskEither.sequenceList(list); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, list.length); - }); - - test('Left', () async { - var sideEffect = 0; - final list = [ - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right<String, int>(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return left<String, int>("Error"); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right<String, int>(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right<String, int>(4); - }), - ]; - final traverse = TaskEither.sequenceList(list); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - - group('traverseList', () { - test('Right', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskEither.traverseList<String, int, String>( - list, - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right<String, String>("$a"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, list.length); - }); - - test('Left', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskEither.traverseList<String, int, String>( - list, - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 - ? right<String, String>("$a") - : left<String, String>("Error"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - - group('traverseListWithIndex', () { - test('Right', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskEither.traverseListWithIndex<String, int, String>( - list, - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right<String, String>("$a$i"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, list.length); - }); - - test('Left', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskEither.traverseListWithIndex<String, int, String>( - list, - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 - ? right<String, String>("$a$i") - : left<String, String>("Error"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, list.length); - }); - }); - - group('sequenceListSeq', () { - test('Right', () async { - var sideEffect = 0; - final list = [ - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 0; - return right<String, int>(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return right<String, int>(2); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return right<String, int>(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return right<String, int>(4); - }), - ]; - final traverse = TaskEither.sequenceListSeq(list); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, 3); - }); - - test('Left', () async { - var sideEffect = 0; - final list = [ - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 0; - return right<String, int>(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return left<String, int>("Error"); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return right<String, int>(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return right<String, int>(4); - }), - ]; - final traverse = TaskEither.sequenceListSeq(list); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, 3); - }); - }); - - group('traverseListSeq', () { - test('Right', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskEither.traverseListSeq<String, int, String>( - list, - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return right<String, String>("$a"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, 5); - }); - - test('Left', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskEither.traverseListSeq<String, int, String>( - list, - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return a % 2 == 0 - ? right<String, String>("$a") - : left<String, String>("Error"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, 5); - }); - }); - - group('traverseListWithIndexSeq', () { - test('Right', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = - TaskEither.traverseListWithIndexSeq<String, int, String>( - list, - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return right<String, String>("$a$i"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestRight((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, 11); - }); - - test('Left', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = - TaskEither.traverseListWithIndexSeq<String, int, String>( - list, - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return a % 2 == 0 - ? right<String, String>("$a$i") - : left<String, String>("Error"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - expect(sideEffect, 11); - }); - }); - - group('Do Notation', () { - test('should return the correct value', () async { - final doTaskEither = - TaskEither<String, int>.Do((_) => _(TaskEither.of(10))); - final run = await doTaskEither.run(); - run.matchTestRight((t) { - expect(t, 10); - }); - }); - - test('should extract the correct values', () async { - final doTaskEither = TaskEither<String, int>.Do((_) async { - final a = await _(TaskEither.of(10)); - final b = await _(TaskEither.of(5)); - return a + b; - }); - final run = await doTaskEither.run(); - run.matchTestRight((t) { - expect(t, 15); - }); - }); - - test('should return Left if any Either is Left', () async { - final doTaskEither = TaskEither<String, int>.Do((_) async { - final a = await _(TaskEither.of(10)); - final b = await _(TaskEither.of(5)); - final c = await _(TaskEither<String, int>.left('Error')); - return a + b + c; - }); - final run = await doTaskEither.run(); - run.matchTestLeft((t) { - expect(t, 'Error'); - }); - }); - - test('should rethrow if throw is used inside Do', () { - final doTaskEither = TaskEither<String, int>.Do((_) { - _(TaskEither.of(10)); - throw UnimplementedError(); - }); - - expect( - doTaskEither.run, throwsA(const TypeMatcher<UnimplementedError>())); - }); - - test('should rethrow if Left is thrown inside Do', () { - final doTaskEither = TaskEither<String, int>.Do((_) { - _(TaskEither.of(10)); - throw Left('Error'); - }); - - expect(doTaskEither.run, throwsA(const TypeMatcher<Left>())); - }); - - test('should no execute past the first Left', () async { - var mutable = 10; - final doTaskEitherLeft = TaskEither<String, int>.Do((_) async { - final a = await _(TaskEither.of(10)); - final b = await _(TaskEither<String, int>.left("Error")); - mutable += 10; - return a + b; - }); - - final runLeft = await doTaskEitherLeft.run(); - expect(mutable, 10); - runLeft.matchTestLeft((l) { - expect(l, "Error"); - }); - - final doTaskEitherRight = TaskEither<String, int>.Do((_) async { - final a = await _(TaskEither.of(10)); - mutable += 10; - return a; - }); - - final runRight = await doTaskEitherRight.run(); - expect(mutable, 20); - runRight.matchTestRight((t) { - expect(t, 10); - }); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/task_option_test.dart b/packages/fpdart/test/src/task_option_test.dart deleted file mode 100644 index 0c1a727..0000000 --- a/packages/fpdart/test/src/task_option_test.dart +++ /dev/null @@ -1,748 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import './utils/utils.dart'; - -void main() { - group('TaskOption', () { - group('tryCatch', () { - test('Success', () async { - final task = TaskOption<int>.tryCatch(() => Future.value(10)); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('Failure', () async { - final task = TaskOption<int>.tryCatch(() => Future.error(10)); - final r = await task.run(); - expect(r, isA<None>()); - }); - - test('throws Exception', () async { - final task = TaskOption<int>.tryCatch(() { - throw UnimplementedError(); - }); - final r = await task.run(); - expect(r, isA<None>()); - }); - }); - - group('tryCatchK', () { - test('Success', () async { - final task = TaskOption<int>.of(10); - final ap = task.flatMap(TaskOption.tryCatchK( - (n) => Future.value(n + 5), - )); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 15)); - }); - - test('Failure', () async { - final task = TaskOption<int>.of(10); - final ap = task.flatMap(TaskOption.tryCatchK( - (n) => Future<int>.error(n + 5), - )); - final r = await ap.run(); - expect(r, isA<None>()); - }); - - test('throws Exception', () async { - final task = TaskOption<int>.of(10); - final ap = task.flatMap(TaskOption.tryCatchK<int, int>((_) { - throw UnimplementedError(); - })); - final r = await ap.run(); - expect(r, isA<None>()); - }); - }); - - group('flatMap', () { - test('Some', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ap = - task.flatMap((r) => TaskOption<int>(() async => Option.of(r + 10))); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 20)); - }); - - test('None', () async { - final task = TaskOption<int>(() async => Option.none()); - final ap = - task.flatMap((r) => TaskOption<int>(() async => Option.of(r + 10))); - final r = await ap.run(); - expect(r, isA<None>()); - }); - }); - - group('ap', () { - test('Some', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ap = task - .ap<double>(TaskOption(() async => Option.of((int c) => c / 2))); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 5.0)); - }); - - test('None', () async { - final task = TaskOption<int>(() async => Option.none()); - final ap = task - .ap<double>(TaskOption(() async => Option.of((int c) => c / 2))); - final r = await ap.run(); - expect(r, isA<None>()); - }); - }); - - group('map', () { - test('Some', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ap = task.map((r) => r / 2); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 5.0)); - }); - - test('None', () async { - final task = TaskOption<int>(() async => Option.none()); - final ap = task.map((r) => r / 2); - final r = await ap.run(); - expect(r, isA<None>()); - }); - }); - - group('map2', () { - test('Some', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ap = task.map2<int, double>( - TaskOption<int>(() async => Option.of(2)), (b, c) => b / c); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 5.0)); - }); - - test('None', () async { - final task = TaskOption<int>(() async => Option.none()); - final ap = task.map2<int, double>( - TaskOption<int>(() async => Option.of(2)), (b, c) => b / c); - final r = await ap.run(); - expect(r, isA<None>()); - }); - }); - - group('map3', () { - test('Some', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ap = task.map3<int, int, double>( - TaskOption<int>(() async => Option.of(2)), - TaskOption<int>(() async => Option.of(5)), - (b, c, d) => b * c / d); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 4.0)); - }); - - test('None', () async { - final task = TaskOption<int>(() async => Option.none()); - final ap = task.map3<int, int, double>( - TaskOption<int>(() async => Option.of(2)), - TaskOption<int>(() async => Option.of(5)), - (b, c, d) => b * c / d); - final r = await ap.run(); - expect(r, isA<None>()); - }); - }); - - group('andThen', () { - test('Some', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ap = - task.andThen(() => TaskOption<double>(() async => Option.of(12.5))); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 12.5)); - }); - - test('None', () async { - final task = TaskOption<int>(() async => Option.none()); - final ap = - task.andThen(() => TaskOption<double>(() async => Option.of(12.5))); - final r = await ap.run(); - expect(r, isA<None>()); - }); - }); - - group('call', () { - test('Some', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ap = task(TaskOption<double>(() async => Option.of(12.5))); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 12.5)); - }); - - test('None', () async { - final task = TaskOption<int>(() async => Option.none()); - final ap = task(TaskOption<double>(() async => Option.of(12.5))); - final r = await ap.run(); - expect(r, isA<None>()); - }); - }); - - test('pure', () async { - final task = TaskOption<int>(() async => Option.none()); - final ap = task.pure('abc'); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 'abc')); - }); - - test('run', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final future = task.run(); - expect(future, isA<Future>()); - final r = await future; - r.matchTestSome((r) => expect(r, 10)); - }); - - group('fromEither', () { - test('Some', () async { - final task = TaskOption.fromEither<String, int>(Either.of(10)); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('None', () async { - final task = TaskOption.fromEither<String, int>(Either.left('none')); - final r = await task.run(); - expect(r, isA<None>()); - }); - }); - - group('fromNullable', () { - test('Right', () async { - final task = TaskOption<int>.fromNullable(10); - final result = await task.run(); - result.matchTestSome((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final task = TaskOption<int>.fromNullable(null); - final result = await task.run(); - expect(result, isA<None>()); - }); - }); - - group('fromPredicate', () { - test('True', () async { - final task = TaskOption<int>.fromPredicate(20, (n) => n > 10); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 20)); - }); - - test('False', () async { - final task = TaskOption<int>.fromPredicate(10, (n) => n > 10); - final r = await task.run(); - expect(r, isA<None>()); - }); - }); - - test('fromTask', () async { - final task = TaskOption<int>.fromTask(Task(() async => 10)); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('none()', () async { - final task = TaskOption<int>.none(); - final r = await task.run(); - expect(r, isA<None>()); - }); - - test('some()', () async { - final task = TaskOption<int>.some(10); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - group('match', () { - test('Some', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ex = task.match(() => -1, (r) => r + 10); - final r = await ex.run(); - expect(r, 20); - }); - - test('None', () async { - final task = TaskOption<int>(() async => Option.none()); - final ex = task.match(() => -1, (r) => r + 10); - final r = await ex.run(); - expect(r, -1); - }); - }); - - group('getOrElse', () { - test('Some', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ex = task.getOrElse(() => -1); - final r = await ex.run(); - expect(r, 10); - }); - - test('None', () async { - final task = TaskOption<int>(() async => Option.none()); - final ex = task.getOrElse(() => -1); - final r = await ex.run(); - expect(r, -1); - }); - }); - - group('orElse', () { - test('Some', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ex = - task.orElse<int>(() => TaskOption(() async => Option.of(-1))); - final r = await ex.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('None', () async { - final task = TaskOption<int>(() async => Option.none()); - final ex = - task.orElse<int>(() => TaskOption(() async => Option.of(-1))); - final r = await ex.run(); - r.matchTestSome((r) => expect(r, -1)); - }); - }); - - group('alt', () { - test('Some', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ex = task.alt(() => TaskOption(() async => Option.of(20))); - final r = await ex.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('None', () async { - final task = TaskOption<int>(() async => Option.none()); - final ex = task.alt(() => TaskOption(() async => Option.of(20))); - final r = await ex.run(); - r.matchTestSome((r) => expect(r, 20)); - }); - }); - - test('of', () async { - final task = TaskOption<int>.of(10); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('flatten', () async { - final task = TaskOption<TaskOption<int>>.of(TaskOption<int>.of(10)); - final ap = TaskOption.flatten(task); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('delay', () async { - final task = TaskOption<int>(() async => Option.of(10)); - final ap = task.delay(const Duration(seconds: 2)); - final stopwatch = Stopwatch(); - stopwatch.start(); - await ap.run(); - stopwatch.stop(); - expect(stopwatch.elapsedMilliseconds >= 2000, true); - }); - - group('toTaskEither', () { - test('Some', () async { - final task = TaskOption(() async => Option.of(10)); - final convert = task.toTaskEither(() => 'None'); - final r = await convert.run(); - r.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('None', () async { - final task = TaskOption(() async => const Option.none()); - final convert = task.toTaskEither(() => 'None'); - final r = await convert.run(); - r.matchTestLeft((l) { - expect(l, 'None'); - }); - }); - }); - - group('sequenceList', () { - test('Some', () async { - var sideEffect = 0; - final list = [ - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some(1); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some(2); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some(3); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some(4); - }), - ]; - final traverse = TaskOption.sequenceList(list); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, list.length); - }); - - test('None', () async { - var sideEffect = 0; - final list = [ - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some(1); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return none<int>(); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some(3); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some(4); - }), - ]; - final traverse = TaskOption.sequenceList(list); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - - group('traverseList', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseList<int, String>( - list, - (a) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some("$a"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, list.length); - }); - - test('None', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseList<int, String>( - list, - (a) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 ? some("$a") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - - group('traverseListWithIndex', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseListWithIndex<int, String>( - list, - (a, i) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return some("$a$i"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, list.length); - }); - - test('None', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseListWithIndex<int, String>( - list, - (a, i) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 ? some("$a$i") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, list.length); - }); - }); - - group('sequenceListSeq', () { - test('Some', () async { - var sideEffect = 0; - final list = [ - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 0; - return some(1); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return some(2); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return some(3); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return some(4); - }), - ]; - final traverse = TaskOption.sequenceListSeq(list); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, [1, 2, 3, 4]); - }); - expect(sideEffect, 3); - }); - - test('None', () async { - var sideEffect = 0; - final list = [ - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 0; - return some(1); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return none<int>(); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return some(3); - }), - TaskOption(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return some(4); - }), - ]; - final traverse = TaskOption.sequenceListSeq(list); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, 3); - }); - }); - - group('traverseListSeq', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseListSeq<int, String>( - list, - (a) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return some("$a"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, ['1', '2', '3', '4', '5', '6']); - }); - expect(sideEffect, 5); - }); - - test('None', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseListSeq<int, String>( - list, - (a) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return a % 2 == 0 ? some("$a") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, 5); - }); - }); - - group('traverseListWithIndexSeq', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseListWithIndexSeq<int, String>( - list, - (a, i) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return some("$a$i"); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - result.matchTestSome((t) { - expect(t, ['10', '21', '32', '43', '54', '65']); - }); - expect(sideEffect, 11); - }); - - test('None', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseListWithIndexSeq<int, String>( - list, - (a, i) => TaskOption( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return a % 2 == 0 ? some("$a$i") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, isA<None>()); - expect(sideEffect, 11); - }); - }); - - group('Do Notation', () { - test('should return the correct value', () async { - final doTaskOption = TaskOption<int>.Do((_) => _(TaskOption.of(10))); - final run = await doTaskOption.run(); - run.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('should extract the correct values', () async { - final doTaskOption = TaskOption<int>.Do((_) async { - final a = await _(TaskOption.of(10)); - final b = await _(TaskOption.of(5)); - return a + b; - }); - final run = await doTaskOption.run(); - run.matchTestSome((t) { - expect(t, 15); - }); - }); - - test('should return Left if any Either is Left', () async { - final doTaskOption = TaskOption<int>.Do((_) async { - final a = await _(TaskOption.of(10)); - final b = await _(TaskOption.of(5)); - final c = await _(TaskOption<int>.none()); - return a + b + c; - }); - final run = await doTaskOption.run(); - expect(run, isA<None>()); - }); - - test('should rethrow if throw is used inside Do', () { - final doTaskOption = TaskOption<int>.Do((_) { - _(TaskOption.of(10)); - throw UnimplementedError(); - }); - - expect( - doTaskOption.run, throwsA(const TypeMatcher<UnimplementedError>())); - }); - - test('should rethrow if None is thrown inside Do', () { - final doTaskOption = TaskOption<int>.Do((_) { - _(TaskOption.of(10)); - throw const None(); - }); - - expect(doTaskOption.run, throwsA(const TypeMatcher<None>())); - }); - - test('should no execute past the first Left', () async { - var mutable = 10; - final doTaskOptionNone = TaskOption<int>.Do((_) async { - final a = await _(TaskOption.of(10)); - final b = await _(TaskOption<int>.none()); - mutable += 10; - return a + b; - }); - - final runNone = await doTaskOptionNone.run(); - expect(mutable, 10); - expect(runNone, isA<None>()); - - final doTaskOptionSome = TaskOption<int>.Do((_) async { - final a = await _(TaskOption.of(10)); - mutable += 10; - return a; - }); - - final runSome = await doTaskOptionSome.run(); - expect(mutable, 20); - runSome.matchTestSome((t) { - expect(t, 10); - }); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/task_test.dart b/packages/fpdart/test/src/task_test.dart deleted file mode 100644 index 22c78c6..0000000 --- a/packages/fpdart/test/src/task_test.dart +++ /dev/null @@ -1,318 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -import 'utils/utils.dart'; - -void main() { - group('Task', () { - group('map', () { - test('int to int', () async { - final task = Task(() async => 10); - final ap = task.map((a) => a + 1); - expect(ap, isA<Task<int>>()); - final r = await ap.run(); - expect(r, 11); - }); - - test('String to int', () async { - final task = Task(() async => 'abc'); - final ap = task.map((a) => a.length); - expect(ap, isA<Task<int>>()); - final r = await ap.run(); - expect(r, 3); - }); - }); - - test('ap', () async { - final task = Task(() async => 10); - final ap = task.ap(Task(() async => (int a) => a + 1)); - expect(ap, isA<Task>()); - final r = await ap.run(); - expect(r, 11); - }); - - test('flatMap', () async { - final task = Task(() async => 10); - final ap = task - .flatMap((a) => Task(() async => '$a')) - .flatMap((a) => Task(() async => a.length)); - expect(ap, isA<Task>()); - final r = await ap.run(); - expect(r, 2); - }); - - test('pure', () async { - final task = Task(() async => 10); - final ap = task.pure('abc'); - final r = await ap.run(); - expect(r, 'abc'); - }); - - test('map2', () async { - final task = Task(() async => 10); - final m2 = task.map2<String, int>(Task.of('abc'), (a, c) => a + c.length); - final r = await m2.run(); - expect(r, 13); - }); - - test('map3', () async { - final task = Task(() async => 10); - final m3 = task.map3<int, String, double>( - Task.of(2), Task.of('abc'), (a, c, d) => (a + d.length) / c); - final r = await m3.run(); - expect(r, 6.5); - }); - - test('delay', () async { - final task = Task(() async => 10); - final ap = task.delay(const Duration(seconds: 2)); - final stopwatch = Stopwatch(); - stopwatch.start(); - await ap.run(); - stopwatch.stop(); - expect(stopwatch.elapsedMilliseconds >= 2000, true); - }); - - test('of', () async { - final task = Task.of(10); - final r = await task.run(); - expect(r, 10); - }); - - test('run', () async { - final task = Task.of(10); - final future = task.run(); - expect(future, isA<Future<int>>()); - final r = await future; - expect(r, 10); - }); - - test('flatten', () async { - final task = Task.of(Task.of(10)); - final t1 = await task.run(); - final t2 = await t1.run(); - final ap = Task.flatten(task); - final r = await ap.run(); - expect(r, 10); - expect(t2, 10); - expect(r, t2); - }); - - group('andThen', () { - test('run a Task after another Task', () async { - final task = Task(() async => 10); - final ap = task.andThen(() => Task.of('abc')); - final r = await ap.run(); - expect(r, 'abc'); - }); - - test('never run the second Task since the first throws', () async { - final task = Task(() async => throw UnimplementedError()); - final ap = task.andThen(() => Task.of('abc')); - expect(ap.run, throwsA(const TypeMatcher<UnimplementedError>())); - }); - }); - - group('call', () { - test('run a Task after another Task', () async { - final task = Task(() async => 10); - final ap = task(Task.of('abc')); - final r = await ap.run(); - expect(r, 'abc'); - }); - - test('run the second Task but return throw error', () async { - final task = Task<String>(() async => throw UnimplementedError()); - final ap = task(Task.of('abc')); - expect(ap.run, throwsA(const TypeMatcher<UnimplementedError>())); - }); - }); - - test('toTaskOption', () async { - final io = Task.of(10); - final convert = io.toTaskOption(); - final r = await convert.run(); - r.matchTestSome((r) { - expect(r, 10); - }); - }); - - test('toTaskEither', () async { - final io = Task.of(10); - final convert = io.toTaskEither(); - final r = await convert.run(); - r.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('sequenceList', () async { - var sideEffect = 0; - final list = [ - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return 1; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return 2; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return 3; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return 4; - }), - ]; - final traverse = Task.sequenceList(list); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, [1, 2, 3, 4]); - expect(sideEffect, list.length); - }); - - test('sequenceListSeq', () async { - var sideEffect = 0; - final list = [ - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect = 0; - return 1; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return 2; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return 3; - }), - Task(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return 4; - }), - ]; - final traverse = Task.sequenceListSeq(list); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, [1, 2, 3, 4]); - expect(sideEffect, 3); - }); - - test('traverseList', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = Task.traverseList<int, String>( - list, - (a) => Task( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return "$a"; - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, ['1', '2', '3', '4', '5', '6']); - expect(sideEffect, list.length); - }); - - test('traverseListWithIndex', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = Task.traverseListWithIndex<int, String>( - list, - (a, i) => Task( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return "$a$i"; - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, ['10', '21', '32', '43', '54', '65']); - expect(sideEffect, list.length); - }); - - test('traverseListSeq', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = Task.traverseListSeq<int, String>( - list, - (a) => Task( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a; - return "$a"; - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, ['1', '2', '3', '4', '5', '6']); - expect(sideEffect, 6); - }); - - test('traverseListWithIndexSeq', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = Task.traverseListWithIndexSeq<int, String>( - list, - (a, i) => Task( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return "$a$i"; - }, - ), - ); - expect(sideEffect, 0); - final result = await traverse.run(); - expect(result, ['10', '21', '32', '43', '54', '65']); - expect(sideEffect, 11); - }); - - group('Do Notation', () { - test('should return the correct value', () async { - final doTask = Task.Do((_) => _(Task.of(10))); - final run = await doTask.run(); - expect(run, 10); - }); - - test('should extract the correct values', () async { - final doTask = Task.Do((_) async { - final a = await _(Task.of(10)); - final b = await _(Task.of(5)); - return a + b; - }); - final run = await doTask.run(); - expect(run, 15); - }); - - test('should not execute until run is called', () async { - var mutable = 10; - final doTask = Task.Do((_) async { - final a = await _(Task.of(10)); - final b = await _(Task.of(5)); - mutable += 10; - return a + b; - }); - expect(mutable, 10); - final run = await doTask.run(); - expect(mutable, 20); - expect(run, 15); - }); - }); - }); -} diff --git a/packages/fpdart/test/src/unit_test.dart b/packages/fpdart/test/src/unit_test.dart deleted file mode 100644 index 01db0aa..0000000 --- a/packages/fpdart/test/src/unit_test.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:fpdart/src/unit.dart'; -import 'package:test/test.dart'; - -void main() { - group('Unit', () { - test('only one instance', () { - const unit1 = unit; - const unit2 = unit; - expect(unit1, unit2); - expect(unit1.hashCode, unit2.hashCode); - }); - - test('toString', () async { - expect(unit.toString(), '()'); - }); - }); -} diff --git a/packages/fpdart/test/src/utils/async_utils.dart b/packages/fpdart/test/src/utils/async_utils.dart deleted file mode 100644 index f2e535c..0000000 --- a/packages/fpdart/test/src/utils/async_utils.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'dart:math'; - -abstract class AsyncUtils { - /// Wait a random number of milliseconds between - /// 0 and `maxMilliseconds`. - static Future<void> waitFuture({ - /// Max number of milliseconds to wait - int maxMilliseconds = 300, - }) => - Future.delayed( - Duration( - milliseconds: Random().nextInt(maxMilliseconds), - ), - ); -} diff --git a/packages/fpdart/test/src/utils/collection_utils.dart b/packages/fpdart/test/src/utils/collection_utils.dart deleted file mode 100644 index fa90b14..0000000 --- a/packages/fpdart/test/src/utils/collection_utils.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:test/test.dart'; - -final objectDeepEquality = const DeepCollectionEquality().equals; - -void testImmutableMap<K, V>( - Map<K, V> source, - void Function(Map<K, V> value) test, -) { - final originalSource = {...source}; - test(source); - expect( - objectDeepEquality(originalSource, source), - true, - reason: - "The provided element is not immutable: ${source} should be ${originalSource}", - ); -} diff --git a/packages/fpdart/test/src/utils/glados_utils.dart b/packages/fpdart/test/src/utils/glados_utils.dart deleted file mode 100644 index 16e6e8f..0000000 --- a/packages/fpdart/test/src/utils/glados_utils.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:glados/glados.dart'; - -extension AnyOption on Any { - /// `glados` [Generator] for [Option] of any type [T], given - /// a generator for type [T]. - /// - /// `ratioNone` defines the ratio of [None] in the test (default 10%). - Generator<Option<T>> optionGenerator<T>( - Generator<T> source, { - double ratioNone = 0.1, - }) => - (random, size) => (random.nextDouble() > ratioNone - ? source.map(some) - : source.map((value) => none<T>()))(random, size); - - /// [Generator] for `Option<int>` - Generator<Option<int>> get optionInt => optionGenerator(any.int); - - /// [Generator] for `Option<double>` - Generator<Option<double>> get optionDouble => optionGenerator(any.double); - - /// [Generator] for `Option<String>` - Generator<Option<String>> get optionString => - optionGenerator(any.letterOrDigits); -} - -extension AnyEither on Any { - /// `glados` [Generator] for [Either] of any type [L] and [R], given - /// a generator for type [L] and [R]. - /// - /// `ratioLeft` defines the ratio of [Left] in the test (default 50%). - Generator<Either<L, R>> eitherGenerator<L, R>( - Generator<L> leftSource, - Generator<R> rightSource, { - double ratioLeft = 0.5, - }) => - (random, size) => (random.nextDouble() > ratioLeft - ? leftSource.map<Either<L, R>>(left) - : rightSource.map<Either<L, R>>(right))(random, size); - - /// [Generator] for `Either<String, int>` - Generator<Either<String, int>> get eitherStringInt => - eitherGenerator(any.letterOrDigits, any.int); -} diff --git a/packages/fpdart/test/src/utils/match_utils.dart b/packages/fpdart/test/src/utils/match_utils.dart deleted file mode 100644 index 1d6c0bc..0000000 --- a/packages/fpdart/test/src/utils/match_utils.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:fpdart/fpdart.dart'; -import 'package:test/test.dart'; - -extension OptionMatch<T> on Option<T> { - /// Run test on [Some], call `fail` if [None]. - void matchTestSome(void Function(T t) testing) => match(() { - fail("should be some, found none"); - }, testing); -} - -extension EitherMatch<L, R> on Either<L, R> { - /// Run test on [Right], call `fail` if [Left]. - void matchTestRight(void Function(R r) testing) => match((l) { - fail("should be right, found left ('$l')"); - }, testing); - - /// Run test on [Left], call `fail` if [Right]. - void matchTestLeft(void Function(L l) testing) => match(testing, (r) { - fail("should be left, found right ('$r')"); - }); -} diff --git a/packages/fpdart/test/src/utils/utils.dart b/packages/fpdart/test/src/utils/utils.dart deleted file mode 100644 index f05a017..0000000 --- a/packages/fpdart/test/src/utils/utils.dart +++ /dev/null @@ -1,6 +0,0 @@ -export 'package:glados/glados.dart'; - -export './async_utils.dart'; -export './collection_utils.dart'; -export './glados_utils.dart'; -export './match_utils.dart'; From 5163c52bc338d3f9596d4b59efe538605005418d Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 13:32:35 +0900 Subject: [PATCH 19/91] initial (new) tests on `Effect` --- packages/fpdart/example/effect/main.dart | 2 +- packages/fpdart/lib/src/effect.dart | 37 +++++++++++++++---- packages/fpdart/lib/src/either.dart | 33 +++++++++++++++++ packages/fpdart/lib/src/exit.dart | 12 ++++++ .../src/extension/future_or_extension.dart | 11 ++++++ packages/fpdart/lib/src/option.dart | 34 +++++++++++++++++ .../src/effect/effect_constructors_test.dart | 21 +++++++++++ 7 files changed, 141 insertions(+), 9 deletions(-) create mode 100644 packages/fpdart/lib/src/extension/future_or_extension.dart create mode 100644 packages/fpdart/test/src/effect/effect_constructors_test.dart diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index 0f8232d..da615b8 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -23,6 +23,6 @@ void main() async { print(main); - final run = await main(10); + final run = await main.runFutureExit(10); print(run); } diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 274bf44..75fa87b 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -1,8 +1,10 @@ import 'dart:async'; +import 'package:fpdart/fpdart.dart'; +import 'package:fpdart/src/extension/future_or_extension.dart'; import 'package:meta/meta.dart'; -import 'exit.dart'; +import 'unit.dart' as FUnit; part 'either.dart'; part 'option.dart'; @@ -49,9 +51,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category execution} Future<Exit<L, R>> _runEffect(E? env) async => _unsafeRun(env); - /// {@category execution} - Future<Exit<L, R>> call(E env) => _runEffect(env); - /// {@category execution} R runSync(E env) { final result = _unsafeRun(env); @@ -65,6 +64,15 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { }; } + /// {@category execution} + Exit<L, R> runSyncExit(E env) { + final result = _unsafeRun(env); + if (result is Future) { + throw Exception("Cannot use runSync for an async Effect"); + } + return result; + } + /// {@category execution} Future<R> runFuture(E env) async { final result = await _unsafeRun(env); @@ -74,6 +82,9 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { }; } + /// {@category execution} + Future<Exit<L, R>> runFutureExit(E env) async => _runEffect(env); + /// {@category constructors} // ignore: non_constant_identifier_names factory Effect.gen(DoFunctionEffect<E, L, R> f) => Effect<E, L, R>._( @@ -107,14 +118,14 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category constructors} - factory Effect.fail(L value) => Effect._((_) async => Exit.failure(value)); + factory Effect.fail(L value) => Effect._((_) => Exit.failure(value)); /// {@category constructors} - factory Effect.succeed(R value) => Effect._((_) async => Exit.success(value)); + factory Effect.succeed(R value) => Effect._((_) => Exit.success(value)); /// {@category constructors} - static Effect<Never, Never, void> unit() => Effect._( - (_) async => Exit.success(null), + static Effect<Never, Never, FUnit.Unit> unit() => Effect._( + (_) => Exit.success(FUnit.unit), ); /// Extract the required dependency from the complete environment. @@ -139,6 +150,16 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); + /// {@category mapping} + Effect<E, R, L> get flip => Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Failure(value: final value) => Exit.success(value), + Success(value: final value) => Exit.failure(value), + }, + ), + ); + /// {@category mapping} Effect<E, L, V> map<V>(V Function(R r) f) => ap(Effect.succeed(f)); diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index 51b7a63..0ca2940 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -3,6 +3,9 @@ part of "effect.dart"; sealed class Either<L, R> extends IEffect<Never, L, R> { const Either(); + R? toNullable(); + Option<R> toOption(); + Either<L, C> flatMap<C>(covariant Either<L, C> Function(R r) f) { return switch (this) { Left(value: final value) => Left(value), @@ -45,6 +48,21 @@ final class Right<L, R> extends Either<L, R> { Either<C, R> orElse<C>(covariant Either<C, R> Function(L l) orElse) => Right(value); + + @override + R toNullable() => value; + + @override + Option<R> toOption() => Some(value); + + @override + bool operator ==(Object other) => (other is Right) && other.value == value; + + @override + int get hashCode => value.hashCode; + + @override + String toString() => 'Right($value)'; } final class Left<L, R> extends Either<L, R> { @@ -59,4 +77,19 @@ final class Left<L, R> extends Either<L, R> { Either<C, R> orElse<C>(covariant Either<C, R> Function(L l) orElse) => orElse(value); + + @override + R? toNullable() => null; + + @override + Option<R> toOption() => None(); + + @override + bool operator ==(Object other) => (other is Left) && other.value == value; + + @override + int get hashCode => value.hashCode; + + @override + String toString() => 'Left($value)'; } diff --git a/packages/fpdart/lib/src/exit.dart b/packages/fpdart/lib/src/exit.dart index 3088948..131ceb1 100644 --- a/packages/fpdart/lib/src/exit.dart +++ b/packages/fpdart/lib/src/exit.dart @@ -8,6 +8,12 @@ class Success<L, R> extends Exit<L, R> { final R value; const Success(this.value); + @override + bool operator ==(Object other) => (other is Success) && other.value == value; + + @override + int get hashCode => value.hashCode; + @override String toString() { return "Exit.Success($value)"; @@ -18,6 +24,12 @@ class Failure<L, R> extends Exit<L, R> { final L value; const Failure(this.value); + @override + bool operator ==(Object other) => (other is Failure) && other.value == value; + + @override + int get hashCode => value.hashCode; + @override String toString() { return "Exit.Failure($value)"; diff --git a/packages/fpdart/lib/src/extension/future_or_extension.dart b/packages/fpdart/lib/src/extension/future_or_extension.dart new file mode 100644 index 0000000..578a99b --- /dev/null +++ b/packages/fpdart/lib/src/extension/future_or_extension.dart @@ -0,0 +1,11 @@ +import 'dart:async'; + +extension FutureOrThenExtension<A> on FutureOr<A> { + FutureOr<B> then<B>(FutureOr<B> Function(A a) f) { + if (this is Future) { + return (this as Future<A>).then(f); + } + + return f(this as A); + } +} diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index 6525bcb..613dccc 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -3,6 +3,13 @@ part of "effect.dart"; sealed class Option<R> extends IEffect<Never, Never, R> { const Option(); + R? toNullable(); + + Either<L, R> toEither<L>(L Function() onLeft) => switch (this) { + Some(value: final value) => Right(value), + None() => Left(onLeft()), + }; + Option<C> flatMap<C>(covariant Option<C> Function(R r) f) { return switch (this) { None() => None(), @@ -37,6 +44,21 @@ final class Some<R> extends Option<R> { Effect<Never, Never, R> get asEffect => Effect.succeed(value); Option<C> andThen<C>(covariant Option<C> Function() then) => then(); + + @override + R toNullable() => value; + + @override + Either<L, R> toEither<L>(L Function() onLeft) => Right(value); + + @override + bool operator ==(Object other) => (other is Some) && other.value == value; + + @override + int get hashCode => value.hashCode; + + @override + String toString() => 'Some($value)'; } final class None extends Option<Never> { @@ -50,4 +72,16 @@ final class None extends Option<Never> { Effect<Never, Never, Never> get asEffect => Effect.fail(null as Never); Option<C> andThen<C>(covariant Option<C> Function() then) => this; + + @override + Null toNullable() => null; + + @override + bool operator ==(Object other) => other is None; + + @override + int get hashCode => 0; + + @override + String toString() => 'None'; } diff --git a/packages/fpdart/test/src/effect/effect_constructors_test.dart b/packages/fpdart/test/src/effect/effect_constructors_test.dart new file mode 100644 index 0000000..d209ad6 --- /dev/null +++ b/packages/fpdart/test/src/effect/effect_constructors_test.dart @@ -0,0 +1,21 @@ +import "package:fpdart/fpdart.dart"; +import "package:test/test.dart"; + +void main() { + group( + "Effect constructors", + () { + test('succeed', () { + final main = Effect.succeed(10); + final result = main.runSync(null); + expect(result, 10); + }); + + test('fail', () { + final main = Effect.fail(10); + final result = main.flip.runSync(null); + expect(result, 10); + }); + }, + ); +} From 51a2a62cab3535b7ad92cbfc93b7d83043d44059 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 13:33:31 +0900 Subject: [PATCH 20/91] import alias --- packages/fpdart/lib/src/effect.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 75fa87b..a7b2a54 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -4,7 +4,7 @@ import 'package:fpdart/fpdart.dart'; import 'package:fpdart/src/extension/future_or_extension.dart'; import 'package:meta/meta.dart'; -import 'unit.dart' as FUnit; +import 'unit.dart' as f_unit; part 'either.dart'; part 'option.dart'; @@ -124,8 +124,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { factory Effect.succeed(R value) => Effect._((_) => Exit.success(value)); /// {@category constructors} - static Effect<Never, Never, FUnit.Unit> unit() => Effect._( - (_) => Exit.success(FUnit.unit), + static Effect<Never, Never, f_unit.Unit> unit() => Effect._( + (_) => Exit.success(f_unit.unit), ); /// Extract the required dependency from the complete environment. From b5b4c4cdd75b2a10c487d71ad6b11a73c4863fc8 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 16:02:04 +0900 Subject: [PATCH 21/91] `Effect` sync execute --- packages/fpdart/lib/src/effect.dart | 132 ++++++++++++------ .../src/effect/effect_collecting_test.dart | 18 +++ 2 files changed, 111 insertions(+), 39 deletions(-) create mode 100644 packages/fpdart/test/src/effect/effect_collecting_test.dart diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index a7b2a54..9f968db 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:fpdart/fpdart.dart'; import 'package:fpdart/src/extension/future_or_extension.dart'; +import 'package:fpdart/src/extension/iterable_extension.dart'; import 'package:meta/meta.dart'; import 'unit.dart' as f_unit; @@ -17,7 +18,7 @@ final class _EffectThrow<L> { typedef DoAdapterEffect<E, L> = Future<A> Function<A>(IEffect<E, L, A>); DoAdapterEffect<E, L> _doAdapter<E, L>(E? env) => <A>(effect) => Future.sync( - () => effect.asEffect._runEffect(env).then( + () => effect.asEffect._unsafeRun(env).then( (exit) => switch (exit) { Failure(value: final value) => throw _EffectThrow(value), Success(value: final value) => value, @@ -48,9 +49,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { return "Effect(${_unsafeRun.runtimeType})"; } - /// {@category execution} - Future<Exit<L, R>> _runEffect(E? env) async => _unsafeRun(env); - /// {@category execution} R runSync(E env) { final result = _unsafeRun(env); @@ -83,7 +81,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { } /// {@category execution} - Future<Exit<L, R>> runFutureExit(E env) async => _runEffect(env); + Future<Exit<L, R>> runFutureExit(E env) async => _unsafeRun(env); /// {@category constructors} // ignore: non_constant_identifier_names @@ -103,9 +101,9 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { L Function(Object error, StackTrace stackTrace) onError, ) => Effect._( - (env) async { + (env) { try { - return Exit.success(await execute()); + return execute().then(Exit.success); } catch (e, s) { return Exit.failure(onError(e, s)); } @@ -114,7 +112,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category constructors} factory Effect.function(FutureOr<R> Function() f) => Effect._( - (_) async => Exit.success(await f()), + (_) => f().then(Exit.success), ); /// {@category constructors} @@ -128,6 +126,56 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (_) => Exit.success(f_unit.unit), ); + /// {@category collecting} + static Effect<E, L, List<R>> allIterable<E, L, R, A>( + Iterable<A> iterable, + Effect<E, L, R> Function(A _) f, + ) => + Effect._( + (env) { + if (iterable.isEmpty) { + return Exit.success([]); + } + + return iterable + .map(f) + .fold<Effect<E, L, Iterable<R>>>( + Effect.succeed(Iterable.empty()), + (acc, effect) => acc.zipWith( + effect, + (list, r) => list.append(r), + ), + ) + ._unsafeRun(env) + .then( + (exit) => switch (exit) { + Failure(value: final value) => Exit.failure(value), + Success(value: final value) => Exit.success(value.toList()), + }, + ); + }, + ); + + /// {@category collecting} + static Effect<E, L, List<R>> all<E, L, R>( + Iterable<Effect<E, L, R>> iterable, + ) => + Effect.allIterable( + iterable, + identity, + ); + + /// {@category zipping} + Effect<E, L, C> zipWith<B, C>( + Effect<E, L, B> effect, + C Function(R r, B b) f, + ) => + flatMap( + (r) => effect.map( + (b) => f(r, b), + ), + ); + /// Extract the required dependency from the complete environment. /// /// {@category do_notation} @@ -137,7 +185,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category do_notation} static Effect<E, L, E> env<E, L>() => Effect._( - (env) async => Exit.success(env!), + (env) => Exit.success(env!), ); /// {@category combining} @@ -165,27 +213,31 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category mapping} Effect<E, C, R> mapError<C>(C Function(L l) f) => Effect._( - (env) async => switch ((await _runEffect(env))) { - Failure(value: final value) => Exit.failure(f(value)), - Success(value: final value) => Exit.success(value), - }, + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Failure(value: final value) => Exit.failure(f(value)), + Success(value: final value) => Exit.success(value), + }, + ), ); /// {@category mapping} Effect<E, C, D> mapBoth<C, D>(C Function(L l) fl, D Function(R r) fr) => Effect._( - (env) async => switch ((await _runEffect(env))) { - Failure(value: final value) => Exit.failure(fl(value)), - Success(value: final value) => Exit.success(fr(value)), - }, + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Failure(value: final value) => Exit.failure(fl(value)), + Success(value: final value) => Exit.success(fr(value)), + }, + ), ); /// {@category sequencing} Effect<E, L, C> flatMap<C>(Effect<E, L, C> Function(R r) f) => Effect._( - (env) => _runEffect(env).then( - (exit) async => switch (exit) { + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { Failure(value: final value) => Future.value(Exit.failure(value)), - Success(value: final value) => f(value)._runEffect(env), + Success(value: final value) => f(value)._unsafeRun(env), }, ), ); @@ -196,15 +248,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category sequencing} Effect<E, L, R> tapError<C>(Effect<E, C, R> Function(L l) f) => Effect._( - (env) async { - switch ((await _runEffect(env))) { - case Failure(value: final value): - await f(value)._unsafeRun(env); - return Exit<L, R>.failure(value); - case Success(value: final value): - return Exit.success(value); - } - }, + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Failure(value: final value) => + f(value)._unsafeRun(env).then((_) => Exit.failure(value)), + Success(value: final value) => Exit.success(value), + }, + ), ); /// {@category sequencing} @@ -216,11 +266,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, C, R> Function(L l) orElse, ) => Effect._( - (env) async => switch ((await _unsafeRun(env))) { - Failure(value: final value) => orElse(value)._unsafeRun(env), - Success(value: final value) => - Effect<E, C, R>.succeed(value)._unsafeRun(env), - }, + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Failure(value: final value) => orElse(value)._unsafeRun(env), + Success(value: final value) => + Effect<E, C, R>.succeed(value)._unsafeRun(env), + }, + ), ); /// {@category error_handling} @@ -228,11 +280,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, Never, R> Function(L error) f, ) => Effect._( - (env) async => switch ((await _unsafeRun(env))) { - Failure(value: final value) => f(value)._unsafeRun(env), - Success(value: final value) => - Effect<E, Never, R>.succeed(value)._unsafeRun(env), - }, + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Failure(value: final value) => f(value)._unsafeRun(env), + Success(value: final value) => + Effect<E, Never, R>.succeed(value)._unsafeRun(env), + }, + ), ); } diff --git a/packages/fpdart/test/src/effect/effect_collecting_test.dart b/packages/fpdart/test/src/effect/effect_collecting_test.dart new file mode 100644 index 0000000..0c0c72f --- /dev/null +++ b/packages/fpdart/test/src/effect/effect_collecting_test.dart @@ -0,0 +1,18 @@ +import "package:fpdart/fpdart.dart"; +import "package:test/test.dart"; + +void main() { + group( + "Effect collecting", + () { + test('all', () { + final main = Effect.all([ + Effect.succeed(10), + Effect.succeed(20), + ]); + final result = main.runSync(null); + expect(result, [10, 20]); + }); + }, + ); +} From c2e9349e4b192b200f3092084ac931050b66df0f Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 16:03:51 +0900 Subject: [PATCH 22/91] collect as `Iterable` --- packages/fpdart/lib/src/effect.dart | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 9f968db..052fbba 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -127,7 +127,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category collecting} - static Effect<E, L, List<R>> allIterable<E, L, R, A>( + static Effect<E, L, Iterable<R>> allIterable<E, L, R, A>( Iterable<A> iterable, Effect<E, L, R> Function(A _) f, ) => @@ -146,18 +146,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (list, r) => list.append(r), ), ) - ._unsafeRun(env) - .then( - (exit) => switch (exit) { - Failure(value: final value) => Exit.failure(value), - Success(value: final value) => Exit.success(value.toList()), - }, - ); + ._unsafeRun(env); }, ); /// {@category collecting} - static Effect<E, L, List<R>> all<E, L, R>( + static Effect<E, L, Iterable<R>> all<E, L, R>( Iterable<Effect<E, L, R>> iterable, ) => Effect.allIterable( From 7fc0a246d36fcc6a2a4e376e1b6b5d57bb2d407c Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 16:11:36 +0900 Subject: [PATCH 23/91] expand `Effect` API --- packages/fpdart/lib/src/effect.dart | 11 +++++-- .../src/effect/effect_sequencing_test.dart | 29 +++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 packages/fpdart/test/src/effect/effect_sequencing_test.dart diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 052fbba..2367171 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -5,7 +5,7 @@ import 'package:fpdart/src/extension/future_or_extension.dart'; import 'package:fpdart/src/extension/iterable_extension.dart'; import 'package:meta/meta.dart'; -import 'unit.dart' as f_unit; +import 'unit.dart' as fpdart_unit; part 'either.dart'; part 'option.dart'; @@ -110,6 +110,11 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { }, ); + /// {@category constructors} + factory Effect.fromNullable(R? value, L Function() onNull) => Effect._( + (_) => value == null ? Exit.failure(onNull()) : Exit.success(value), + ); + /// {@category constructors} factory Effect.function(FutureOr<R> Function() f) => Effect._( (_) => f().then(Exit.success), @@ -122,8 +127,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { factory Effect.succeed(R value) => Effect._((_) => Exit.success(value)); /// {@category constructors} - static Effect<Never, Never, f_unit.Unit> unit() => Effect._( - (_) => Exit.success(f_unit.unit), + static Effect<Never, Never, fpdart_unit.Unit> unit() => Effect._( + (_) => Exit.success(fpdart_unit.unit), ); /// {@category collecting} diff --git a/packages/fpdart/test/src/effect/effect_sequencing_test.dart b/packages/fpdart/test/src/effect/effect_sequencing_test.dart new file mode 100644 index 0000000..1bfdbfa --- /dev/null +++ b/packages/fpdart/test/src/effect/effect_sequencing_test.dart @@ -0,0 +1,29 @@ +import "package:fpdart/fpdart.dart"; +import "package:test/test.dart"; + +void main() { + group( + "Effect constructors", + () { + test('andThen', () { + final main = Effect.succeed(10).andThen(() => Effect.succeed("10")); + final result = main.runSync(null); + expect(result, "10"); + }); + + test('tap', () { + var mutable = 0; + final main = Effect.succeed(10).tap( + (_) => Effect.function(() { + mutable += 1; + }), + ); + + expect(mutable, 0); + final result = main.runSync(null); + expect(result, 10); + expect(mutable, 1); + }); + }, + ); +} From f70327f880b5e392865730c630c4c8926d64d7be Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 16:20:47 +0900 Subject: [PATCH 24/91] `orDie` API --- packages/fpdart/lib/src/effect.dart | 24 ++++++++++++ .../src/effect/effect_alternatives_test.dart | 37 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 packages/fpdart/test/src/effect/effect_alternatives_test.dart diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 2367171..805406d 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -274,6 +274,30 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); + /// {@category alternatives} + Effect<E, Never, R> get orDie => Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Failure(value: final value) => + throw Exception("orDie effect ($value)"), + Success(value: final value) => + Effect<E, Never, R>.succeed(value)._unsafeRun(env), + }, + ), + ); + + /// {@category alternatives} + Effect<E, Never, R> orDieWith<T extends Object>(T Function(L l) onError) => + Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Failure(value: final value) => throw onError(value), + Success(value: final value) => + Effect<E, Never, R>.succeed(value)._unsafeRun(env), + }, + ), + ); + /// {@category error_handling} Effect<E, Never, R> catchError( Effect<E, Never, R> Function(L error) f, diff --git a/packages/fpdart/test/src/effect/effect_alternatives_test.dart b/packages/fpdart/test/src/effect/effect_alternatives_test.dart new file mode 100644 index 0000000..d0f4504 --- /dev/null +++ b/packages/fpdart/test/src/effect/effect_alternatives_test.dart @@ -0,0 +1,37 @@ +import "package:fpdart/fpdart.dart"; +import "package:test/test.dart"; + +class CustomError implements Exception {} + +void main() { + group( + "Effect alternatives", + () { + group('orDie', () { + test('succeed', () { + final main = Effect.succeed(10).orDie; + final result = main.runSync(null); + expect(result, 10); + }); + + test('fail', () { + final main = Effect.fail(10).orDie; + expect(() => main.runSync(null), throwsException); + }); + }); + + group('orDieWith', () { + test('succeed', () { + final main = Effect.succeed(10).orDieWith((_) => CustomError()); + final result = main.runSync(null); + expect(result, 10); + }); + + test('fail', () { + final main = Effect.fail(10).orDieWith((_) => CustomError()); + expect(() => main.runSync(null), throwsA(isA<CustomError>())); + }); + }); + }, + ); +} From e66dd028711621e6c8102f9704783edd54091c3c Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 16:34:19 +0900 Subject: [PATCH 25/91] `Option` API --- .../lib/src/extension/iterable_extension.dart | 12 ++-- .../lib/src/extension/map_extension.dart | 6 +- packages/fpdart/lib/src/option.dart | 59 ++++++++++++++----- packages/fpdart/test/src/option_test.dart | 19 ++++++ 4 files changed, 71 insertions(+), 25 deletions(-) create mode 100644 packages/fpdart/test/src/option_test.dart diff --git a/packages/fpdart/lib/src/extension/iterable_extension.dart b/packages/fpdart/lib/src/extension/iterable_extension.dart index 04060f1..8d8a13c 100644 --- a/packages/fpdart/lib/src/extension/iterable_extension.dart +++ b/packages/fpdart/lib/src/extension/iterable_extension.dart @@ -19,7 +19,7 @@ extension FpdartOnIterable<T> on Iterable<T> { Option<T> get head { var it = iterator; if (it.moveNext()) return Some(it.current); - return const None(); + return None(); } /// {@macro fpdart_iterable_extension_head} @@ -33,7 +33,7 @@ extension FpdartOnIterable<T> on Iterable<T> { /// **Note**: Because accessing the last element of an [Iterable] requires /// stepping through all the other elements, `lastOption` **can be slow**. Option<T> get lastOption { - if (isEmpty) return const None(); + if (isEmpty) return None(); return Some(last); } @@ -47,7 +47,7 @@ extension FpdartOnIterable<T> on Iterable<T> { /// at that point, the returned iterable will also be empty, same /// as if this iterable has only one element. Option<Iterable<T>> get tail { - if (isEmpty) return const None(); + if (isEmpty) return None(); return Some(skip(1)); } @@ -61,7 +61,7 @@ extension FpdartOnIterable<T> on Iterable<T> { /// at that point, the returned iterable will also be empty, same /// as if this iterable has only one element. Option<Iterable<T>> get init { - if (isEmpty) return const None(); + if (isEmpty) return None(); return Some(this.dropRight(1)); } @@ -332,7 +332,7 @@ extension FpdartOnIterable<T> on Iterable<T> { } return Some(min); } - return const None(); + return None(); } /// The least element of this [Iterable] based on `order`. @@ -349,7 +349,7 @@ extension FpdartOnIterable<T> on Iterable<T> { } return Some(min); } - return const None(); + return None(); } /// Apply all the functions inside `iterable` to this [Iterable]. diff --git a/packages/fpdart/lib/src/extension/map_extension.dart b/packages/fpdart/lib/src/extension/map_extension.dart index a5bf0f7..25b8deb 100644 --- a/packages/fpdart/lib/src/extension/map_extension.dart +++ b/packages/fpdart/lib/src/extension/map_extension.dart @@ -55,7 +55,7 @@ extension FpdartOnMap<K, V> on Map<K, V> { var value = this[key]; if (value != null) return Some(value); if (containsKey(key)) return Some(value as V); - return const None(); + return None(); } /// Get the value and key at given `key` if present, otherwise return [None]. @@ -63,7 +63,7 @@ extension FpdartOnMap<K, V> on Map<K, V> { final value = this[key]; if (value != null) return Some((key, value)); if (containsKey(key)) return Some((key, value as V)); - return const None(); + return None(); } /// Return an [Option] that conditionally accesses map keys, only if they match the @@ -78,7 +78,7 @@ extension FpdartOnMap<K, V> on Map<K, V> { Option<T> extract<T>(K key) { final value = this[key]; if (value is T) return Some(value); - return const None(); + return None(); } /// Return an [Option] that conditionally accesses map keys if they contain a value diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index 613dccc..f9c5f88 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -3,20 +3,36 @@ part of "effect.dart"; sealed class Option<R> extends IEffect<Never, Never, R> { const Option(); - R? toNullable(); + factory Option.safeCast(dynamic value) => + Option.safeCastStrict<R, dynamic>(value); - Either<L, R> toEither<L>(L Function() onLeft) => switch (this) { - Some(value: final value) => Right(value), - None() => Left(onLeft()), - }; + static Option<R> safeCastStrict<R, V>(V value) { + if (value is R) return Some(value); + return None(); + } + + factory Option.fromPredicate(R value, bool Function(R r) predicate) { + if (predicate(value)) return Some(value); + return None(); + } + + factory Option.fromNullable(R? value) { + if (value != null) return Some(value); + return None(); + } - Option<C> flatMap<C>(covariant Option<C> Function(R r) f) { - return switch (this) { - None() => None(), - Some(value: final value) => f(value), - }; + factory Option.tryCatch(R Function() f) { + try { + return Some(f()); + } catch (_) { + return None(); + } } + R? toNullable(); + + Option<C> flatMap<C>(covariant Option<C> Function(R r) f); + Option<V> ap<V>( covariant Option<V Function(R r)> f, ) => @@ -26,6 +42,11 @@ sealed class Option<R> extends IEffect<Never, Never, R> { ), ); + Either<L, R> toEither<L>(L Function() onLeft) => switch (this) { + Some(value: final value) => Right(value), + None() => Left(onLeft()), + }; + Option<V> map<V>(V Function(R r) f) => ap(Some(f)); Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect._( @@ -45,6 +66,12 @@ final class Some<R> extends Option<R> { Option<C> andThen<C>(covariant Option<C> Function() then) => then(); + @override + Option<C> flatMap<C>(covariant Option<C> Function(R r) f) => f(value); + + @override + Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect.succeed(value); + @override R toNullable() => value; @@ -62,7 +89,10 @@ final class Some<R> extends Option<R> { } final class None extends Option<Never> { - const None(); + static const None _none = None._instance(); + const None._instance(); + + factory None() => _none; @override @internal @@ -74,13 +104,10 @@ final class None extends Option<Never> { Option<C> andThen<C>(covariant Option<C> Function() then) => this; @override - Null toNullable() => null; - - @override - bool operator ==(Object other) => other is None; + Option<C> flatMap<C>(covariant Option<C> Function(Never r) f) => this; @override - int get hashCode => 0; + Null toNullable() => null; @override String toString() => 'None'; diff --git a/packages/fpdart/test/src/option_test.dart b/packages/fpdart/test/src/option_test.dart new file mode 100644 index 0000000..c2fb63b --- /dev/null +++ b/packages/fpdart/test/src/option_test.dart @@ -0,0 +1,19 @@ +import "package:fpdart/fpdart.dart"; +import "package:test/test.dart"; + +class CustomError implements Exception {} + +void main() { + group( + "Option", + () { + group('None', () { + test('singleton', () { + final none1 = None(); + final none2 = None(); + expect(none1, none2); + }); + }); + }, + ); +} From 15b56342e3c2aa5be639e0ab2c3b9523ef7b8e3b Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 16:46:21 +0900 Subject: [PATCH 26/91] `Either` API --- packages/fpdart/lib/src/either.dart | 118 +++++++++++++++++++++------- packages/fpdart/lib/src/option.dart | 13 ++- 2 files changed, 97 insertions(+), 34 deletions(-) diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index 0ca2940..591eab5 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -3,18 +3,55 @@ part of "effect.dart"; sealed class Either<L, R> extends IEffect<Never, L, R> { const Either(); + /// If calling `predicate` with `r` returns `true`, then return `Right(r)`. + /// Otherwise return [Left] containing the result of `onFalse`. + factory Either.fromPredicate( + R r, + bool Function(R r) predicate, + L Function(R r) onFalse, + ) => + predicate(r) ? Right(r) : Left(onFalse(r)); + + factory Either.fromNullable(R? r, L Function() onNull) => + r != null ? Right(r) : Left(onNull()); + + factory Either.tryCatch( + R Function() run, + L Function(Object o, StackTrace s) onError, + ) { + try { + return Right(run()); + } catch (e, s) { + return Left(onError(e, s)); + } + } + + factory Either.safeCast( + dynamic value, + L Function(dynamic value) onError, + ) => + Either.safeCastStrict<L, R, dynamic>(value, onError); + + static Either<L, R> safeCastStrict<L, R, V>( + V value, + L Function(V value) onError, + ) => + value is R ? Right(value) : Left(onError(value)); + R? toNullable(); Option<R> toOption(); - - Either<L, C> flatMap<C>(covariant Either<L, C> Function(R r) f) { - return switch (this) { - Left(value: final value) => Left(value), - Right(value: final value) => f(value), - }; - } + Either<L, C> flatMap<C>(Either<L, C> Function(R r) f); + Either<C, R> mapLeft<C>(C Function(L l) f); + Effect<V, L, R> provide<V>(); + Either<D, C> mapBoth<C, D>({ + required D Function(L l) onLeft, + required C Function(R r) onRight, + }); + Either<R, L> get flip; + R getOrElse(R Function(L l) orElse); Either<L, V> ap<V>( - covariant Either<L, V Function(R r)> f, + Either<L, V Function(R r)> f, ) => f.flatMap( (f) => flatMap( @@ -23,18 +60,6 @@ sealed class Either<L, R> extends IEffect<Never, L, R> { ); Either<L, V> map<V>(V Function(R r) f) => ap(Right(f)); - - Effect<V, L, R> provide<V>() => Effect._( - (env) => switch (this) { - Left(value: final value) => Exit.failure(value), - Right(value: final value) => Exit.success(value), - }, - ); - - Either<C, R> mapLeft<C>(C Function(L l) f) => switch (this) { - Left(value: final value) => Left(f(value)), - Right(value: final value) => Right(value), - }; } final class Right<L, R> extends Either<L, R> { @@ -44,10 +69,30 @@ final class Right<L, R> extends Either<L, R> { @override Effect<Never, L, R> get asEffect => Effect.succeed(value); - Either<L, C> andThen<C>(covariant Either<L, C> Function() then) => then(); + Either<L, C> andThen<C>(Either<L, C> Function() then) => then(); + + Either<C, R> orElse<C>(Either<C, R> Function(L l) orElse) => Right(value); + + @override + R getOrElse(R Function(L l) orElse) => value; + + @override + Either<R, L> get flip => Left(value); + + @override + Either<D, C> mapBoth<C, D>( + {required D Function(L l) onLeft, + required C Function(R r) onRight}) => + Right(onRight(value)); - Either<C, R> orElse<C>(covariant Either<C, R> Function(L l) orElse) => - Right(value); + @override + Either<C, R> mapLeft<C>(C Function(L l) f) => Right(value); + + @override + Either<L, C> flatMap<C>(Either<L, C> Function(R r) f) => f(value); + + @override + Effect<V, L, R> provide<V>() => Effect.succeed(value); @override R toNullable() => value; @@ -72,11 +117,30 @@ final class Left<L, R> extends Either<L, R> { @override Effect<Never, L, R> get asEffect => Effect.fail(value); - Either<L, C> andThen<C>(covariant Either<L, C> Function() then) => - Left(value); + Either<L, C> andThen<C>(Either<L, C> Function() then) => Left(value); + + Either<C, R> orElse<C>(Either<C, R> Function(L l) orElse) => orElse(value); + + @override + R getOrElse(R Function(L l) orElse) => orElse(value); + + @override + Either<R, L> get flip => Right(value); - Either<C, R> orElse<C>(covariant Either<C, R> Function(L l) orElse) => - orElse(value); + @override + Either<D, C> mapBoth<C, D>( + {required D Function(L l) onLeft, + required C Function(R r) onRight}) => + Left(onLeft(value)); + + @override + Either<C, R> mapLeft<C>(C Function(L l) f) => Left(f(value)); + + @override + Either<L, C> flatMap<C>(Either<L, C> Function(R r) f) => Left(value); + + @override + Effect<V, L, R> provide<V>() => Effect.fail(value); @override R? toNullable() => null; diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index f9c5f88..fe80a82 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -30,11 +30,10 @@ sealed class Option<R> extends IEffect<Never, Never, R> { } R? toNullable(); - - Option<C> flatMap<C>(covariant Option<C> Function(R r) f); + Option<C> flatMap<C>(Option<C> Function(R r) f); Option<V> ap<V>( - covariant Option<V Function(R r)> f, + Option<V Function(R r)> f, ) => f.flatMap( (f) => flatMap( @@ -64,10 +63,10 @@ final class Some<R> extends Option<R> { @override Effect<Never, Never, R> get asEffect => Effect.succeed(value); - Option<C> andThen<C>(covariant Option<C> Function() then) => then(); + Option<C> andThen<C>(Option<C> Function() then) => then(); @override - Option<C> flatMap<C>(covariant Option<C> Function(R r) f) => f(value); + Option<C> flatMap<C>(Option<C> Function(R r) f) => f(value); @override Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect.succeed(value); @@ -101,10 +100,10 @@ final class None extends Option<Never> { // ignore: cast_from_null_always_fails Effect<Never, Never, Never> get asEffect => Effect.fail(null as Never); - Option<C> andThen<C>(covariant Option<C> Function() then) => this; + Option<C> andThen<C>(Option<C> Function() then) => this; @override - Option<C> flatMap<C>(covariant Option<C> Function(Never r) f) => this; + Option<C> flatMap<C>(Option<C> Function(Never r) f) => this; @override Null toNullable() => null; From cb69054bd72c199ea78827b24749d6769d4a052a Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 17 Mar 2024 20:29:36 +0900 Subject: [PATCH 27/91] `Exit` with `Cause` --- packages/fpdart/lib/src/effect.dart | 130 ++++++++++++------ packages/fpdart/lib/src/exit.dart | 100 +++++++++++--- packages/fpdart/lib/src/option.dart | 4 +- .../src/effect/effect_alternatives_test.dart | 4 +- 4 files changed, 174 insertions(+), 64 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 805406d..87cb08a 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -11,8 +11,8 @@ part 'either.dart'; part 'option.dart'; final class _EffectThrow<L> { - final L value; - const _EffectThrow(this.value); + final Cause<L> cause; + const _EffectThrow(this.cause); } typedef DoAdapterEffect<E, L> = Future<A> Function<A>(IEffect<E, L, A>); @@ -20,8 +20,8 @@ typedef DoAdapterEffect<E, L> = Future<A> Function<A>(IEffect<E, L, A>); DoAdapterEffect<E, L> _doAdapter<E, L>(E? env) => <A>(effect) => Future.sync( () => effect.asEffect._unsafeRun(env).then( (exit) => switch (exit) { - Failure(value: final value) => throw _EffectThrow(value), - Success(value: final value) => value, + Left(value: final cause) => throw _EffectThrow(cause), + Right(value: final value) => value, }, ), ); @@ -38,8 +38,14 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// /// In practice a user of the library should never be allowed to pass `null` as [E]. final FutureOr<Exit<L, R>> Function(E? env) _unsafeRun; + final StackTrace? stackTrace; - const Effect._(this._unsafeRun); + static bool debugTracing = false; + + const Effect._( + this._unsafeRun, { + this.stackTrace, + }); @override Effect<E, L, R> get asEffect => this; @@ -53,12 +59,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { R runSync(E env) { final result = _unsafeRun(env); if (result is Future) { - throw Exception("Cannot use runSync for an async Effect"); + throw Die.current(result, stackTrace); } return switch (result) { - Failure<L, R>() => throw Exception("Failed runSync Effect"), - Success<L, R>(value: final value) => value, + Left(value: final cause) => throw cause, + Right(value: final value) => value, }; } @@ -66,7 +72,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Exit<L, R> runSyncExit(E env) { final result = _unsafeRun(env); if (result is Future) { - throw Exception("Cannot use runSync for an async Effect"); + return Left(Die.current("")); } return result; } @@ -75,8 +81,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Future<R> runFuture(E env) async { final result = await _unsafeRun(env); return switch (result) { - Failure<L, R>() => throw Exception("Failed runFuture Effect"), - Success<L, R>(value: final value) => value, + Left(value: final cause) => throw cause, + Right(value: final value) => value, }; } @@ -88,9 +94,9 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { factory Effect.gen(DoFunctionEffect<E, L, R> f) => Effect<E, L, R>._( (env) async { try { - return Exit.success(await f(_doAdapter<E, L>(env))); - } on _EffectThrow<L> catch (e) { - return Exit.failure(e.value); + return Right(await f(_doAdapter<E, L>(env))); + } on _EffectThrow<L> catch (err) { + return Left(err.cause); } }, ); @@ -103,32 +109,32 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect._( (env) { try { - return execute().then(Exit.success); - } catch (e, s) { - return Exit.failure(onError(e, s)); + return execute().then(Right.new); + } catch (err, stack) { + return Left(Fail(onError(err, stack), stack)); } }, ); /// {@category constructors} factory Effect.fromNullable(R? value, L Function() onNull) => Effect._( - (_) => value == null ? Exit.failure(onNull()) : Exit.success(value), + (_) => value == null ? Left(Fail(onNull())) : Right(value), ); /// {@category constructors} factory Effect.function(FutureOr<R> Function() f) => Effect._( - (_) => f().then(Exit.success), + (_) => f().then(Right.new), ); /// {@category constructors} - factory Effect.fail(L value) => Effect._((_) => Exit.failure(value)); + factory Effect.fail(L value) => Effect._((_) => Left(Fail(value))); /// {@category constructors} - factory Effect.succeed(R value) => Effect._((_) => Exit.success(value)); + factory Effect.succeed(R value) => Effect._((_) => Right(value)); /// {@category constructors} static Effect<Never, Never, fpdart_unit.Unit> unit() => Effect._( - (_) => Exit.success(fpdart_unit.unit), + (_) => Right(fpdart_unit.unit), ); /// {@category collecting} @@ -139,7 +145,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect._( (env) { if (iterable.isEmpty) { - return Exit.success([]); + return Right([]); } return iterable @@ -184,7 +190,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category do_notation} static Effect<E, L, E> env<E, L>() => Effect._( - (env) => Exit.success(env!), + (env) => Right(env!), ); /// {@category combining} @@ -201,8 +207,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, R, L> get flip => Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { - Failure(value: final value) => Exit.success(value), - Success(value: final value) => Exit.failure(value), + Left(value: final cause) => switch (cause) { + Fail<L>(error: final error) => Right(error), + Empty() => Left(cause), + Interrupt() => Left(cause), + Die() => Left(cause), + }, + Right(value: final value) => Left(Fail(value)), }, ), ); @@ -214,8 +225,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, C, R> mapError<C>(C Function(L l) f) => Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { - Failure(value: final value) => Exit.failure(f(value)), - Success(value: final value) => Exit.success(value), + Left(value: final cause) => switch (cause) { + Fail<L>(error: final error) => Left(Fail(f(error))), + Empty() => Left(cause), + Interrupt() => Left(cause), + Die() => Left(cause), + }, + Right(value: final value) => Right(value), }, ), ); @@ -225,8 +241,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { - Failure(value: final value) => Exit.failure(fl(value)), - Success(value: final value) => Exit.success(fr(value)), + Left(value: final cause) => switch (cause) { + Fail<L>(error: final error) => Left(Fail(fl(error))), + Empty() => Left(cause), + Interrupt() => Left(cause), + Die() => Left(cause), + }, + Right(value: final value) => Right(fr(value)), }, ), ); @@ -235,8 +256,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, L, C> flatMap<C>(Effect<E, L, C> Function(R r) f) => Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { - Failure(value: final value) => Future.value(Exit.failure(value)), - Success(value: final value) => f(value)._unsafeRun(env), + Left(value: final cause) => Left(cause), + Right(value: final value) => f(value)._unsafeRun(env), }, ), ); @@ -249,9 +270,15 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, L, R> tapError<C>(Effect<E, C, R> Function(L l) f) => Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { - Failure(value: final value) => - f(value)._unsafeRun(env).then((_) => Exit.failure(value)), - Success(value: final value) => Exit.success(value), + Left(value: final cause) => switch (cause) { + Fail<L>(error: final error) => f(error)._unsafeRun(env).then( + (_) => Left(Fail(error)), + ), + Empty() => Left(cause), + Interrupt() => Left(cause), + Die() => Left(cause), + }, + Right(value: final value) => Right(value), }, ), ); @@ -267,8 +294,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { - Failure(value: final value) => orElse(value)._unsafeRun(env), - Success(value: final value) => + Left(value: final cause) => switch (cause) { + Fail<L>(error: final error) => orElse(error)._unsafeRun(env), + Empty() => Left(cause), + Interrupt() => Left(cause), + Die() => Left(cause), + }, + Right(value: final value) => Effect<E, C, R>.succeed(value)._unsafeRun(env), }, ), @@ -278,9 +310,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, Never, R> get orDie => Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { - Failure(value: final value) => - throw Exception("orDie effect ($value)"), - Success(value: final value) => + Left(value: final cause) => Left(Die.current(cause, stackTrace)), + Right(value: final value) => Effect<E, Never, R>.succeed(value)._unsafeRun(env), }, ), @@ -291,8 +322,14 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { - Failure(value: final value) => throw onError(value), - Success(value: final value) => + Left(value: final cause) => switch (cause) { + Fail<L>(error: final error) => + Left(Die.current(onError(error))), + Empty() => Left(cause), + Interrupt() => Left(cause), + Die() => Left(cause), + }, + Right(value: final value) => Effect<E, Never, R>.succeed(value)._unsafeRun(env), }, ), @@ -305,8 +342,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { - Failure(value: final value) => f(value)._unsafeRun(env), - Success(value: final value) => + Left(value: final cause) => switch (cause) { + Fail<L>(error: final error) => f(error)._unsafeRun(env), + Empty() => Left(cause), + Interrupt() => Left(cause), + Die() => Left(cause), + }, + Right(value: final value) => Effect<E, Never, R>.succeed(value)._unsafeRun(env), }, ), diff --git a/packages/fpdart/lib/src/exit.dart b/packages/fpdart/lib/src/exit.dart index 131ceb1..cb60a03 100644 --- a/packages/fpdart/lib/src/exit.dart +++ b/packages/fpdart/lib/src/exit.dart @@ -1,37 +1,105 @@ -sealed class Exit<L, R> { - const Exit(); - factory Exit.success(R value) => Success(value); - factory Exit.failure(L value) => Failure(value); +import 'effect.dart'; + +typedef Exit<L, R> = Either<Cause<L>, R>; + +sealed class Cause<L> { + const Cause(); + + StackTrace? get stackTrace; + + Cause<L> withTrace(StackTrace stack); +} + +/// Represents a lack of errors +final class Empty extends Cause<Never> { + @override + final StackTrace? stackTrace; + + const Empty([this.stackTrace]); + + @override + Empty withTrace(StackTrace stack) => stackTrace == null ? Empty(stack) : this; + + @override + String toString() { + return "Cause.Empty()"; + } } -class Success<L, R> extends Exit<L, R> { - final R value; - const Success(this.value); +final class Interrupt extends Cause<Never> { + @override + final StackTrace? stackTrace; + + const Interrupt([this.stackTrace]); + + @override + Interrupt withTrace(StackTrace stack) => + stackTrace == null ? Interrupt(stack) : this; @override - bool operator ==(Object other) => (other is Success) && other.value == value; + bool operator ==(Object other) => + identical(this, other) || + other is Interrupt && runtimeType == other.runtimeType; @override - int get hashCode => value.hashCode; + int get hashCode => 0; @override String toString() { - return "Exit.Success($value)"; + return "Cause.Interrupt()"; } } -class Failure<L, R> extends Exit<L, R> { - final L value; - const Failure(this.value); +/// Failed as a result of a defect (unexpected error) +final class Die extends Cause<Never> { + final dynamic error; + final StackTrace defectStackTrace; + + @override + final StackTrace? stackTrace; + + const Die(this.error, this.defectStackTrace, [this.stackTrace]); + + factory Die.current(dynamic error, [StackTrace? stackTrace]) => + Die(error, StackTrace.current, stackTrace); + + @override + Die withTrace(StackTrace stack) => + stackTrace == null ? Die(error, defectStackTrace, stack) : this; + + @override + bool operator ==(Object other) => (other is Fail) && other.error == error; + + @override + int get hashCode => error.hashCode; + + @override + String toString() { + return "Cause.Die($error)"; + } +} + +/// Failed with an expected error +final class Fail<L> extends Cause<L> { + final L error; + + @override + final StackTrace? stackTrace; + + const Fail(this.error, [this.stackTrace]); + + @override + Fail<L> withTrace(StackTrace stack) => + stackTrace == null ? Fail(error, stack) : this; @override - bool operator ==(Object other) => (other is Failure) && other.value == value; + bool operator ==(Object other) => (other is Fail) && other.error == error; @override - int get hashCode => value.hashCode; + int get hashCode => error.hashCode; @override String toString() { - return "Exit.Failure($value)"; + return "Cause.Fail($error)"; } } diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index fe80a82..9dcbfdf 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -50,8 +50,8 @@ sealed class Option<R> extends IEffect<Never, Never, R> { Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect._( (env) => switch (this) { - None() => Exit.failure(onNone()), - Some(value: final value) => Exit.success(value), + None() => Left(Fail(onNone())), + Some(value: final value) => Right(value), }, ); } diff --git a/packages/fpdart/test/src/effect/effect_alternatives_test.dart b/packages/fpdart/test/src/effect/effect_alternatives_test.dart index d0f4504..c4884a0 100644 --- a/packages/fpdart/test/src/effect/effect_alternatives_test.dart +++ b/packages/fpdart/test/src/effect/effect_alternatives_test.dart @@ -16,7 +16,7 @@ void main() { test('fail', () { final main = Effect.fail(10).orDie; - expect(() => main.runSync(null), throwsException); + expect(() => main.runSync(null), throwsA(isA<Die>())); }); }); @@ -29,7 +29,7 @@ void main() { test('fail', () { final main = Effect.fail(10).orDieWith((_) => CustomError()); - expect(() => main.runSync(null), throwsA(isA<CustomError>())); + expect(() => main.runSync(null), throwsA(isA<Die>())); }); }); }, From 4a85df32b44cbc0d76b423b9457a445854157ba6 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 18 Mar 2024 11:22:05 +0900 Subject: [PATCH 28/91] `tryCatch` API --- packages/fpdart/example/effect/main.dart | 4 +-- packages/fpdart/lib/src/effect.dart | 9 ++--- .../src/effect/effect_constructors_test.dart | 34 +++++++++++++++++++ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart index da615b8..01faff0 100644 --- a/packages/fpdart/example/effect/main.dart +++ b/packages/fpdart/example/effect/main.dart @@ -2,8 +2,8 @@ import 'package:fpdart/fpdart.dart'; void main() async { final effect = Effect<Never, String, int>.tryCatch( - () => Future.value(10), - (error, stackTrace) => "10", + execute: () => Future.value(10), + onError: (error, stackTrace) => "10", ); final effect1 = Effect<Never, String, int>.function(() => 10); diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 87cb08a..df94407 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -102,10 +102,11 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category constructors} - factory Effect.tryCatch( - FutureOr<R> Function() execute, - L Function(Object error, StackTrace stackTrace) onError, - ) => + factory Effect.tryCatch({ + required FutureOr<R> Function() execute, + required L Function(Object error, StackTrace stackTrace) onError, + FutureOr<dynamic> Function()? onCancel, + }) => Effect._( (env) { try { diff --git a/packages/fpdart/test/src/effect/effect_constructors_test.dart b/packages/fpdart/test/src/effect/effect_constructors_test.dart index d209ad6..a63e6aa 100644 --- a/packages/fpdart/test/src/effect/effect_constructors_test.dart +++ b/packages/fpdart/test/src/effect/effect_constructors_test.dart @@ -16,6 +16,40 @@ void main() { final result = main.flip.runSync(null); expect(result, 10); }); + + group('tryCatch', () { + test('executes once', () { + var mutable = 0; + final main = Effect.tryCatch( + execute: () { + mutable += 1; + return 10; + }, + onError: (error, stackTrace) {}, + ); + + main.runSync(null); + expect(mutable, 1); + }); + + test('async', () async { + final main = Effect.tryCatch( + execute: () async => 10, + onError: (error, stackTrace) {}, + ); + final result = await main.runFuture(null); + expect(result, 10); + }); + + test('sync', () { + final main = Effect.tryCatch( + execute: () => 10, + onError: (error, stackTrace) {}, + ); + final result = main.runSync(null); + expect(result, 10); + }); + }); }, ); } From 1b8d00e9f127a153cdadecb3b705bf5d96cef464 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 18 Mar 2024 11:25:27 +0900 Subject: [PATCH 29/91] removed `Cause` by interrupt --- packages/fpdart/lib/src/effect.dart | 7 ------- packages/fpdart/lib/src/exit.dart | 24 ------------------------ 2 files changed, 31 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index df94407..3745cfa 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -211,7 +211,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Left(value: final cause) => switch (cause) { Fail<L>(error: final error) => Right(error), Empty() => Left(cause), - Interrupt() => Left(cause), Die() => Left(cause), }, Right(value: final value) => Left(Fail(value)), @@ -229,7 +228,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Left(value: final cause) => switch (cause) { Fail<L>(error: final error) => Left(Fail(f(error))), Empty() => Left(cause), - Interrupt() => Left(cause), Die() => Left(cause), }, Right(value: final value) => Right(value), @@ -245,7 +243,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Left(value: final cause) => switch (cause) { Fail<L>(error: final error) => Left(Fail(fl(error))), Empty() => Left(cause), - Interrupt() => Left(cause), Die() => Left(cause), }, Right(value: final value) => Right(fr(value)), @@ -276,7 +273,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (_) => Left(Fail(error)), ), Empty() => Left(cause), - Interrupt() => Left(cause), Die() => Left(cause), }, Right(value: final value) => Right(value), @@ -298,7 +294,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Left(value: final cause) => switch (cause) { Fail<L>(error: final error) => orElse(error)._unsafeRun(env), Empty() => Left(cause), - Interrupt() => Left(cause), Die() => Left(cause), }, Right(value: final value) => @@ -327,7 +322,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Fail<L>(error: final error) => Left(Die.current(onError(error))), Empty() => Left(cause), - Interrupt() => Left(cause), Die() => Left(cause), }, Right(value: final value) => @@ -346,7 +340,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Left(value: final cause) => switch (cause) { Fail<L>(error: final error) => f(error)._unsafeRun(env), Empty() => Left(cause), - Interrupt() => Left(cause), Die() => Left(cause), }, Right(value: final value) => diff --git a/packages/fpdart/lib/src/exit.dart b/packages/fpdart/lib/src/exit.dart index cb60a03..0430eb9 100644 --- a/packages/fpdart/lib/src/exit.dart +++ b/packages/fpdart/lib/src/exit.dart @@ -26,30 +26,6 @@ final class Empty extends Cause<Never> { } } -final class Interrupt extends Cause<Never> { - @override - final StackTrace? stackTrace; - - const Interrupt([this.stackTrace]); - - @override - Interrupt withTrace(StackTrace stack) => - stackTrace == null ? Interrupt(stack) : this; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is Interrupt && runtimeType == other.runtimeType; - - @override - int get hashCode => 0; - - @override - String toString() { - return "Cause.Interrupt()"; - } -} - /// Failed as a result of a defect (unexpected error) final class Die extends Cause<Never> { final dynamic error; From 69af3834eb8536401c273ee6d7d92a121c37f92e Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 18 Mar 2024 11:33:08 +0900 Subject: [PATCH 30/91] `zipLeft` and `zipRight` --- packages/fpdart/lib/src/effect.dart | 20 +++++++++++++++---- .../src/effect/effect_sequencing_test.dart | 10 ++++++++-- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 3745cfa..a332229 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -182,6 +182,22 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); + /// {@category zipping} + Effect<E, L, R> zipLeft<C>( + Effect<E, L, C> effect, + ) => + flatMap( + (r) => effect.map( + (_) => r, + ), + ); + + /// {@category zipping} + Effect<E, L, C> zipRight<C>( + Effect<E, L, C> effect, + ) => + flatMap((_) => effect); + /// Extract the required dependency from the complete environment. /// /// {@category do_notation} @@ -280,10 +296,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); - /// {@category sequencing} - Effect<E, L, C> andThen<C>(Effect<E, L, C> Function() then) => - flatMap((_) => then()); - /// {@category alternatives} Effect<E, C, R> orElse<C>( Effect<E, C, R> Function(L l) orElse, diff --git a/packages/fpdart/test/src/effect/effect_sequencing_test.dart b/packages/fpdart/test/src/effect/effect_sequencing_test.dart index 1bfdbfa..3e1db93 100644 --- a/packages/fpdart/test/src/effect/effect_sequencing_test.dart +++ b/packages/fpdart/test/src/effect/effect_sequencing_test.dart @@ -5,8 +5,14 @@ void main() { group( "Effect constructors", () { - test('andThen', () { - final main = Effect.succeed(10).andThen(() => Effect.succeed("10")); + test('zipLeft', () { + final main = Effect.succeed(10).zipLeft(Effect.succeed("10")); + final result = main.runSync(null); + expect(result, 10); + }); + + test('zipRight', () { + final main = Effect.succeed(10).zipRight(Effect.succeed("10")); final result = main.runSync(null); expect(result, "10"); }); From 5b5becaad71852b37d047b21627451f2582247a2 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 18 Mar 2024 11:50:35 +0900 Subject: [PATCH 31/91] `forEach` and `all` --- packages/fpdart/lib/src/effect.dart | 11 +++---- .../src/effect/effect_collecting_test.dart | 29 ++++++++++++++----- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index a332229..ed86c6f 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -139,9 +139,9 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category collecting} - static Effect<E, L, Iterable<R>> allIterable<E, L, R, A>( + static Effect<E, L, Iterable<R>> forEach<E, L, R, A>( Iterable<A> iterable, - Effect<E, L, R> Function(A _) f, + Effect<E, L, R> Function(A a, int index) f, ) => Effect._( (env) { @@ -150,7 +150,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { } return iterable - .map(f) + .mapWithIndex(f) .fold<Effect<E, L, Iterable<R>>>( Effect.succeed(Iterable.empty()), (acc, effect) => acc.zipWith( @@ -166,10 +166,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { static Effect<E, L, Iterable<R>> all<E, L, R>( Iterable<Effect<E, L, R>> iterable, ) => - Effect.allIterable( - iterable, - identity, - ); + Effect.forEach(iterable, (a, _) => a); /// {@category zipping} Effect<E, L, C> zipWith<B, C>( diff --git a/packages/fpdart/test/src/effect/effect_collecting_test.dart b/packages/fpdart/test/src/effect/effect_collecting_test.dart index 0c0c72f..dcc2497 100644 --- a/packages/fpdart/test/src/effect/effect_collecting_test.dart +++ b/packages/fpdart/test/src/effect/effect_collecting_test.dart @@ -5,13 +5,28 @@ void main() { group( "Effect collecting", () { - test('all', () { - final main = Effect.all([ - Effect.succeed(10), - Effect.succeed(20), - ]); - final result = main.runSync(null); - expect(result, [10, 20]); + group('all', () { + test('succeeded all', () { + final main = Effect.all([ + Effect.succeed(10), + Effect.succeed(20), + ]); + final result = main.runSync(null); + expect(result, [10, 20]); + }); + + test('fail and stop execution', () { + var mutable = 0; + final main = Effect.all<dynamic, String, int>([ + Effect.succeed(10), + Effect.fail("10"), + Effect.function(() => mutable += 1), + Effect.fail("0"), + ]); + final result = main.flip.runSync(null); + expect(mutable, 0); + expect(result, "10"); + }); }); }, ); From 6e256c912abdc65041cb5043ec241b009ed86a48 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 18 Mar 2024 15:47:10 +0900 Subject: [PATCH 32/91] migration md --- packages/fpdart/MIGRATION.md | 66 ++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 packages/fpdart/MIGRATION.md diff --git a/packages/fpdart/MIGRATION.md b/packages/fpdart/MIGRATION.md new file mode 100644 index 0000000..034e9bc --- /dev/null +++ b/packages/fpdart/MIGRATION.md @@ -0,0 +1,66 @@ +## From `fpdart` v1 to v2 + +### Problems with v1 +Too many classes (`IO`, `IOOption`, `IOEither`, `Task`, `TaskOption`, `TaskEither`, `Reader`, `State`, `ReaderTaskEither`): +- Similar implementation with different generic parameters = **A lot of duplicated code** (both core and tests) +```dart +/// [IO] 👇 +abstract final class _IOHKT {} + +final class IO<A> extends HKT<_IOHKT, A> + with Functor<_IOHKT, A>, Applicative<_IOHKT, A>, Monad<_IOHKT, A> { + final A Function() _run; +} + +/// [Task] 👇 +abstract final class _TaskHKT {} + +final class Task<A> extends HKT<_TaskHKT, A> + with Functor<_TaskHKT, A>, Applicative<_TaskHKT, A>, Monad<_TaskHKT, A> { + final Future<A> Function() _run; /// 👈 Difference: [Future] here +} +``` +- Code duplication = Hard to maintain, more lines of code, more code to read (and understand) for contributors +- Requires conversion between classes (`from*`, `to*`, e.g. `toTask`, `toTaskEither`) +- Requires having a different `Do` constructor for each class, making the do notation harder to use +- Hard to understand for newcomers, hard to reason with and explain for veterans (and verbose) +- More complex code, less contributors + +**Too much jargon**: methods and classes are using terms from pure functional programming (math), less clear and hard to understand (e.g. `pure`, `Reader`, `chainFirst`, `traverse`, `Endo`). + +Typeclasses do not work well with dart and they cause a lot of overhead to maintain and understand. In fact, they are not necessary to implement the core of fpdart (**they can be removed** 💁🏼♂️). + +Too many "utility functions" that may be considered outside of the scope of fpdart (e.g. `predicate_extension.dart`). + +### fpdart v2 solution: `Effect` +A single `Effect` class that contains the API of all other classes in v1 (similar to `ReaderTaskEither`). + +All Effect-classes derive from the same interface `IEffect`: + +```dart +abstract interface class IEffect<E, L, R> { + const IEffect(); + Effect<E, L, R> get asEffect; +} +``` + +Benefits: +- A lot less code: easier to maintain, contribute, test, understand (a single `effect.dart`) +- No need of conversion methods (a lot less code) +- A single Do notation (implemented as a factory constructor `factory Effect.gen`): the do notation also includes `Option` and `Either` (since both are extend `IEffect`) +- No more jargon: easy to understand method names instead of fp jargon (e.g. `succeed` instead of `pure`) +- Removed all typeclasses and unnecessary utility methods +- Easier to explain and understand (focus on learning a single `Effect` and how it works) +- **Smaller API that allows all the same functionalities as before** +- More resources to focus on better documentation, tests, and examples + +> **Important**: `Effect` comes from the [`effect`](https://www.effect.website/) library (typescript), which itself was inspired from [`ZIO`](https://zio.dev/). +> +> The `Effect` class and methods in `fpdart` are based on `effect` from typescript (similar API and methods names). +> +> Huge thanks also to [Tim Smart](https://github.com/tim-smart) for his initial [`zio.dart`](https://github.com/tim-smart/elemental/blob/main/packages/elemental/lib/src/zio.dart) implementation. + +### Downsides +- ⚠️ **Huge breaking change** ⚠️ +- Nearly all tests need to be rewritten +- Documentation and examples to redo completely \ No newline at end of file From bea693068d41a510b4a0983bc5946c8983e6814a Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 18 Mar 2024 15:51:13 +0900 Subject: [PATCH 33/91] version 2.0.0-dev.1 --- packages/fpdart/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fpdart/pubspec.yaml b/packages/fpdart/pubspec.yaml index 23e63d7..959e5cd 100644 --- a/packages/fpdart/pubspec.yaml +++ b/packages/fpdart/pubspec.yaml @@ -2,7 +2,7 @@ name: fpdart description: > Functional programming in Dart and Flutter. All the main functional programming types and patterns fully documented, tested, and with examples. -version: 1.1.0 +version: 2.0.0-dev.1 homepage: https://www.sandromaglione.com/ repository: https://github.com/SandroMaglione/fpdart author: Maglione Sandro <lass.maglio@gmail.com> From bda39348e0feb61a94d88d8582d6e33bce24f4be Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 18 Mar 2024 17:41:37 +0900 Subject: [PATCH 34/91] http request example --- examples/fpdart_http/lib/api.dart | 30 ++++++++++++++++++++++++ examples/fpdart_http/lib/http_error.dart | 11 +++++++++ examples/fpdart_http/lib/main.dart | 18 ++++++++++++++ examples/fpdart_http/pubspec.yaml | 19 +++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 examples/fpdart_http/lib/api.dart create mode 100644 examples/fpdart_http/lib/http_error.dart create mode 100644 examples/fpdart_http/lib/main.dart create mode 100644 examples/fpdart_http/pubspec.yaml diff --git a/examples/fpdart_http/lib/api.dart b/examples/fpdart_http/lib/api.dart new file mode 100644 index 0000000..73ed164 --- /dev/null +++ b/examples/fpdart_http/lib/api.dart @@ -0,0 +1,30 @@ +import 'package:fpdart/fpdart.dart'; +import 'package:http/http.dart' as http; + +import 'http_error.dart'; + +/// 1️⃣ Define dependencies, errors, response +Effect<http.Client, HttpError, http.Response> get( + Uri url, { + Map<String, String>? headers, +}) => + + /// 2️⃣ Use the Do notation with the `gen` constructor + Effect.gen((_) async { + /// 3️⃣ Extract the dependency using `env` (environment) + final client = await _(Effect.env()); + + /// 4️⃣ Perform a request, catch errors, extract the response + final response = await _(Effect.tryCatch( + execute: () => client.get(url, headers: headers), + onError: (_, __) => const RequestError(), + )); + + /// 5️⃣ Use plain dart code to check for valid status + if (response.statusCode != 200) { + return await _(Effect.fail(const ResponseError())); + } + + /// 6️⃣ Return extracted/valid response + return response; + }); diff --git a/examples/fpdart_http/lib/http_error.dart b/examples/fpdart_http/lib/http_error.dart new file mode 100644 index 0000000..c7f9391 --- /dev/null +++ b/examples/fpdart_http/lib/http_error.dart @@ -0,0 +1,11 @@ +sealed class HttpError { + const HttpError(); +} + +final class RequestError extends HttpError { + const RequestError(); +} + +final class ResponseError extends HttpError { + const ResponseError(); +} diff --git a/examples/fpdart_http/lib/main.dart b/examples/fpdart_http/lib/main.dart new file mode 100644 index 0000000..ce8caf5 --- /dev/null +++ b/examples/fpdart_http/lib/main.dart @@ -0,0 +1,18 @@ +import 'package:fpdart/fpdart.dart'; +import 'package:http/http.dart' as http; + +import 'api.dart'; + +void main() async { + final main = await get( + Uri.https("pokeapi.co", "/api/v2/pokemon/10"), + ) + .tap( + (response) => Effect.function( + () => print(response.body), + ), + ) + .runFuture( + http.Client(), + ); +} diff --git a/examples/fpdart_http/pubspec.yaml b/examples/fpdart_http/pubspec.yaml new file mode 100644 index 0000000..3862015 --- /dev/null +++ b/examples/fpdart_http/pubspec.yaml @@ -0,0 +1,19 @@ +name: fpdart_http +description: > + Example of using fpdart with http. +version: 2.0.0 +homepage: https://www.sandromaglione.com/ +repository: https://github.com/SandroMaglione/fpdart +publish_to: "none" + +environment: + sdk: ">=3.3.0 <4.0.0" + +dependencies: + http: ^1.2.1 + fpdart: + path: ../../packages/fpdart + +dev_dependencies: + lints: ^2.0.1 + test: ^1.23.1 From 156543636964f75b72498c27ee10cb34bddb6fe2 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Tue, 19 Mar 2024 08:32:23 +0900 Subject: [PATCH 35/91] run `Effect` with `Never` env --- packages/fpdart/lib/src/effect.dart | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index ed86c6f..3f36767 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -365,4 +365,38 @@ extension ProvideNever<L, R> on Effect<Never, L, R> { Effect<V, L, R> withEnv<V>() => Effect._( (env) => _unsafeRun(null), ); + + /// {@category execution} + R runSyncNoEnv() { + final result = _unsafeRun(null); + if (result is Future) { + throw Die.current(result, stackTrace); + } + + return switch (result) { + Left(value: final cause) => throw cause, + Right(value: final value) => value, + }; + } + + /// {@category execution} + Exit<L, R> runSyncExitNoEnv() { + final result = _unsafeRun(null); + if (result is Future) { + return Left(Die.current("")); + } + return result; + } + + /// {@category execution} + Future<R> runFutureNoEnv() async { + final result = await _unsafeRun(null); + return switch (result) { + Left(value: final cause) => throw cause, + Right(value: final value) => value, + }; + } + + /// {@category execution} + Future<Exit<L, R>> runFutureExitNoEnv() async => _unsafeRun(null); } From f39ee8bc6ff4ffe4182aa4af9b42ba9effdc5f09 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Tue, 19 Mar 2024 21:53:33 +0900 Subject: [PATCH 36/91] fetch pokemon with validation example --- examples/poke_api/lib/constants.dart | 7 ++++ examples/poke_api/lib/main.dart | 51 ++++++++++++++++++++++++ examples/poke_api/lib/pokemon.dart | 20 ++++++++++ examples/poke_api/lib/pokemon_error.dart | 27 +++++++++++++ examples/poke_api/pubspec.yaml | 18 +++++++++ packages/fpdart/lib/src/either.dart | 10 ++--- 6 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 examples/poke_api/lib/constants.dart create mode 100644 examples/poke_api/lib/main.dart create mode 100644 examples/poke_api/lib/pokemon.dart create mode 100644 examples/poke_api/lib/pokemon_error.dart create mode 100644 examples/poke_api/pubspec.yaml diff --git a/examples/poke_api/lib/constants.dart b/examples/poke_api/lib/constants.dart new file mode 100644 index 0000000..2c794b2 --- /dev/null +++ b/examples/poke_api/lib/constants.dart @@ -0,0 +1,7 @@ +abstract interface class Constants { + static const int minimumPokemonId = 1; + static const int maximumPokemonId = 898; + + static String requestAPIUrl(int pokemonId) => + 'https://pokeapi.co/api/v2/pokemon/$pokemonId'; +} diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart new file mode 100644 index 0000000..0de9c98 --- /dev/null +++ b/examples/poke_api/lib/main.dart @@ -0,0 +1,51 @@ +import 'dart:convert'; + +import 'package:fpdart/fpdart.dart'; +import 'package:poke_api/constants.dart'; +import 'package:poke_api/pokemon.dart'; +import 'package:poke_api/pokemon_error.dart'; + +abstract interface class HttpClient { + String get(Uri uri); +} + +Effect<(HttpClient, JsonCodec), PokemonError, Pokemon> program( + String pokemonId, +) => + Effect.gen(($) async { + final (client, json) = await $(Effect.env()); + + final id = await $( + Either.fromNullable( + int.tryParse(pokemonId), + PokemonIdNotInt.new, + ), + ); + + if (id < Constants.minimumPokemonId && id > Constants.maximumPokemonId) { + return await $(Effect.fail(const InvalidPokemonIdRange())); + } + + final uri = Uri.parse(Constants.requestAPIUrl(id)); + final body = await $(Effect.tryCatch( + execute: () => client.get(uri), + onError: (_, __) => const GetPokemonRequestError(), + )); + + final bodyJson = await $(Either.tryCatch( + execute: () => json.decode(body), + onError: (_, __) => const PokemonJsonDecodeError(), + )); + + final bodyJsonMap = await $<Map<String, dynamic>>( + Either.safeCastStrict( + bodyJson, + (value) => const PokemonJsonInvalidMap(), + ), + ); + + return $(Effect.tryCatch( + execute: () => Pokemon.fromJson(bodyJsonMap), + onError: (_, __) => const PokemonInvalidJsonModel(), + )); + }); diff --git a/examples/poke_api/lib/pokemon.dart b/examples/poke_api/lib/pokemon.dart new file mode 100644 index 0000000..34409b1 --- /dev/null +++ b/examples/poke_api/lib/pokemon.dart @@ -0,0 +1,20 @@ +class Pokemon { + final int id; + final String name; + final int height; + final int weight; + + const Pokemon({ + required this.id, + required this.name, + required this.height, + required this.weight, + }); + + factory Pokemon.fromJson(Map<String, Object?> json) => Pokemon( + id: json['id'] as int, + name: json['name'] as String, + height: json['height'] as int, + weight: json['weight'] as int, + ); +} diff --git a/examples/poke_api/lib/pokemon_error.dart b/examples/poke_api/lib/pokemon_error.dart new file mode 100644 index 0000000..92c65f8 --- /dev/null +++ b/examples/poke_api/lib/pokemon_error.dart @@ -0,0 +1,27 @@ +sealed class PokemonError { + const PokemonError(); +} + +class PokemonIdNotInt extends PokemonError { + const PokemonIdNotInt(); +} + +class InvalidPokemonIdRange extends PokemonError { + const InvalidPokemonIdRange(); +} + +class GetPokemonRequestError extends PokemonError { + const GetPokemonRequestError(); +} + +class PokemonJsonDecodeError extends PokemonError { + const PokemonJsonDecodeError(); +} + +class PokemonJsonInvalidMap extends PokemonError { + const PokemonJsonInvalidMap(); +} + +class PokemonInvalidJsonModel extends PokemonError { + const PokemonInvalidJsonModel(); +} diff --git a/examples/poke_api/pubspec.yaml b/examples/poke_api/pubspec.yaml new file mode 100644 index 0000000..423285d --- /dev/null +++ b/examples/poke_api/pubspec.yaml @@ -0,0 +1,18 @@ +name: poke_api +description: > + Example of using fpdart to fetch pokemon from pokeapi with validation. +version: 2.0.0 +homepage: https://www.sandromaglione.com/ +repository: https://github.com/SandroMaglione/fpdart +publish_to: "none" + +environment: + sdk: ">=3.3.0 <4.0.0" + +dependencies: + fpdart: + path: ../../packages/fpdart + +dev_dependencies: + lints: ^2.0.1 + test: ^1.23.1 diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index 591eab5..5841e66 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -15,12 +15,12 @@ sealed class Either<L, R> extends IEffect<Never, L, R> { factory Either.fromNullable(R? r, L Function() onNull) => r != null ? Right(r) : Left(onNull()); - factory Either.tryCatch( - R Function() run, - L Function(Object o, StackTrace s) onError, - ) { + factory Either.tryCatch({ + required R Function() execute, + required L Function(Object o, StackTrace s) onError, + }) { try { - return Right(run()); + return Right(execute()); } catch (e, s) { return Left(onError(e, s)); } From 81d4b0d8a06e41d0416e7923118069ab15789dc9 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Wed, 20 Mar 2024 20:41:00 +0900 Subject: [PATCH 37/91] run `gen` as `sync` or `async` --- examples/fpdart_http/lib/api.dart | 6 +-- examples/poke_api/lib/main.dart | 14 +++--- packages/fpdart/example/effect/main.dart | 28 ----------- packages/fpdart/lib/src/effect.dart | 47 ++++++++++++++----- .../src/effect/effect_constructors_test.dart | 41 ++++++++++++++++ 5 files changed, 87 insertions(+), 49 deletions(-) delete mode 100644 packages/fpdart/example/effect/main.dart diff --git a/examples/fpdart_http/lib/api.dart b/examples/fpdart_http/lib/api.dart index 73ed164..2c7d600 100644 --- a/examples/fpdart_http/lib/api.dart +++ b/examples/fpdart_http/lib/api.dart @@ -12,17 +12,17 @@ Effect<http.Client, HttpError, http.Response> get( /// 2️⃣ Use the Do notation with the `gen` constructor Effect.gen((_) async { /// 3️⃣ Extract the dependency using `env` (environment) - final client = await _(Effect.env()); + final client = _.sync(Effect.env()); /// 4️⃣ Perform a request, catch errors, extract the response - final response = await _(Effect.tryCatch( + final response = await _.async(Effect.tryCatch( execute: () => client.get(url, headers: headers), onError: (_, __) => const RequestError(), )); /// 5️⃣ Use plain dart code to check for valid status if (response.statusCode != 200) { - return await _(Effect.fail(const ResponseError())); + return _.sync(Effect.fail(const ResponseError())); } /// 6️⃣ Return extracted/valid response diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index 0de9c98..546b055 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -13,9 +13,9 @@ Effect<(HttpClient, JsonCodec), PokemonError, Pokemon> program( String pokemonId, ) => Effect.gen(($) async { - final (client, json) = await $(Effect.env()); + final (client, json) = $.sync(Effect.env()); - final id = await $( + final id = $.sync( Either.fromNullable( int.tryParse(pokemonId), PokemonIdNotInt.new, @@ -23,28 +23,28 @@ Effect<(HttpClient, JsonCodec), PokemonError, Pokemon> program( ); if (id < Constants.minimumPokemonId && id > Constants.maximumPokemonId) { - return await $(Effect.fail(const InvalidPokemonIdRange())); + return $.sync(Effect.fail(const InvalidPokemonIdRange())); } final uri = Uri.parse(Constants.requestAPIUrl(id)); - final body = await $(Effect.tryCatch( + final body = await $.async(Effect.tryCatch( execute: () => client.get(uri), onError: (_, __) => const GetPokemonRequestError(), )); - final bodyJson = await $(Either.tryCatch( + final bodyJson = $.sync(Either.tryCatch( execute: () => json.decode(body), onError: (_, __) => const PokemonJsonDecodeError(), )); - final bodyJsonMap = await $<Map<String, dynamic>>( + final bodyJsonMap = $.sync<Map<String, dynamic>>( Either.safeCastStrict( bodyJson, (value) => const PokemonJsonInvalidMap(), ), ); - return $(Effect.tryCatch( + return $.sync(Effect.tryCatch( execute: () => Pokemon.fromJson(bodyJsonMap), onError: (_, __) => const PokemonInvalidJsonModel(), )); diff --git a/packages/fpdart/example/effect/main.dart b/packages/fpdart/example/effect/main.dart deleted file mode 100644 index 01faff0..0000000 --- a/packages/fpdart/example/effect/main.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:fpdart/fpdart.dart'; - -void main() async { - final effect = Effect<Never, String, int>.tryCatch( - execute: () => Future.value(10), - onError: (error, stackTrace) => "10", - ); - - final effect1 = Effect<Never, String, int>.function(() => 10); - - final main = Effect<int, String, int>.gen( - (_) async { - final env = await _(Effect.env()); - final beforeEnv = await _(effect.withEnv()); - final e1 = await _(effect1.mapError((l) => "null").withEnv()); - - final mapped = await _(effect.map((r) => r + 10).withEnv()); - final asEither = await _(Right<String, int>(10).provide<int>()); - final asOption = await _(Some<int>(10).provide(() => "Some")); - return beforeEnv + mapped + asEither + asOption + e1; - }, - ); - - print(main); - - final run = await main.runFutureExit(10); - print(run); -} diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 3f36767..221c589 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -10,23 +10,48 @@ import 'unit.dart' as fpdart_unit; part 'either.dart'; part 'option.dart'; +typedef EffectGen<E, L> = ({ + FutureOr<A> Function<A>(IEffect<E, L, A>) async, + A Function<A>(IEffect<E, L, A>) sync, +}); + final class _EffectThrow<L> { final Cause<L> cause; const _EffectThrow(this.cause); -} -typedef DoAdapterEffect<E, L> = Future<A> Function<A>(IEffect<E, L, A>); + @override + String toString() { + return "Effect.gen error: $cause"; + } +} -DoAdapterEffect<E, L> _doAdapter<E, L>(E? env) => <A>(effect) => Future.sync( - () => effect.asEffect._unsafeRun(env).then( - (exit) => switch (exit) { - Left(value: final cause) => throw _EffectThrow(cause), - Right(value: final value) => value, - }, +EffectGen<E, L> _effectGen<E, L>(E? env) => ( + async: <A>(effect) => Future.sync( + () => effect.asEffect._unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => throw _EffectThrow(cause), + Right(value: final value) => value, + }, + ), ), + sync: <A>(effect) { + final run = effect.asEffect._unsafeRun(env); + if (run is Future) { + throw _EffectThrow<L>( + Die.current("Cannot execute a Future using sync"), + ); + } + + return switch (run) { + Left(value: final cause) => throw _EffectThrow(cause), + Right(value: final value) => value, + }; + }, ); -typedef DoFunctionEffect<E, L, A> = Future<A> Function(DoAdapterEffect<E, L> _); +typedef DoFunctionEffect<E, L, A> = FutureOr<A> Function( + EffectGen<E, L> $, +); abstract interface class IEffect<E, L, R> { const IEffect(); @@ -92,9 +117,9 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category constructors} // ignore: non_constant_identifier_names factory Effect.gen(DoFunctionEffect<E, L, R> f) => Effect<E, L, R>._( - (env) async { + (env) { try { - return Right(await f(_doAdapter<E, L>(env))); + return f(_effectGen<E, L>(env)).then(Right.new); } on _EffectThrow<L> catch (err) { return Left(err.cause); } diff --git a/packages/fpdart/test/src/effect/effect_constructors_test.dart b/packages/fpdart/test/src/effect/effect_constructors_test.dart index a63e6aa..646c60b 100644 --- a/packages/fpdart/test/src/effect/effect_constructors_test.dart +++ b/packages/fpdart/test/src/effect/effect_constructors_test.dart @@ -49,6 +49,47 @@ void main() { final result = main.runSync(null); expect(result, 10); }); + + group('gen', () { + test('sync succeed', () { + final main = Effect.gen(($) { + final value = $.sync(Effect.succeed(10)); + return value; + }); + final result = main.runSyncNoEnv(); + expect(result, 10); + }); + + test('sync fail', () { + final main = Effect<Never, String, int>.gen(($) { + final value = $.sync(Effect.fail("10")); + return value; + }); + final result = main.flip.runSyncNoEnv(); + expect(result, "10"); + }); + + test('async succeed', () async { + final main = Effect.gen(($) async { + final value = + await $.async(Effect.function(() => Future.value(10))); + return value; + }); + final result = await main.runFutureNoEnv(); + expect(result, 10); + }); + + test('fail when running async as sync', () async { + final main = Effect.gen(($) { + final value = $.sync(Effect.function( + () async => Future.value(10), + )); + return value; + }); + + expect(() => main.runSyncNoEnv(), throwsA(isA<Die>())); + }); + }); }); }, ); From 06fff824a6bb409765ae4523f78180f1af522a4e Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 21 Mar 2024 18:28:27 +0900 Subject: [PATCH 38/91] pokemon api example with `void` type https://github.com/dart-lang/language/issues/3670 --- examples/poke_api/analysis_options.yaml | 11 ++++ examples/poke_api/lib/main.dart | 53 +++++++++++++------ examples/poke_api/lib/pokemon.dart | 5 ++ examples/poke_api/pubspec.yaml | 1 + packages/fpdart/lib/src/effect.dart | 5 ++ packages/fpdart/lib/src/either.dart | 2 +- .../src/extension/future_or_extension.dart | 11 ++-- 7 files changed, 65 insertions(+), 23 deletions(-) create mode 100644 examples/poke_api/analysis_options.yaml diff --git a/examples/poke_api/analysis_options.yaml b/examples/poke_api/analysis_options.yaml new file mode 100644 index 0000000..d875ed0 --- /dev/null +++ b/examples/poke_api/analysis_options.yaml @@ -0,0 +1,11 @@ +include: package:lints/recommended.yaml + +linter: + rules: + annotate_overrides: true + +analyzer: + language: + strict-casts: true + strict-inference: true + strict-raw-types: true diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index 546b055..43657e9 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -1,47 +1,59 @@ import 'dart:convert'; import 'package:fpdart/fpdart.dart'; +import 'package:http/http.dart' as http; import 'package:poke_api/constants.dart'; import 'package:poke_api/pokemon.dart'; import 'package:poke_api/pokemon_error.dart'; abstract interface class HttpClient { - String get(Uri uri); + Effect<void, PokemonError, String> get(Uri uri); } -Effect<(HttpClient, JsonCodec), PokemonError, Pokemon> program( +class Http implements HttpClient { + @override + Effect<void, PokemonError, String> get(Uri uri) => Effect.gen( + ($) async { + final response = await $.async(Effect.tryCatch( + execute: () => http.get(uri), + onError: (error, stackTrace) => const GetPokemonRequestError(), + )); + + return response.body; + }, + ); +} + +typedef Env = (HttpClient, JsonCodec); + +Effect<Env, PokemonError, Pokemon> program( String pokemonId, ) => Effect.gen(($) async { final (client, json) = $.sync(Effect.env()); - final id = $.sync( - Either.fromNullable( - int.tryParse(pokemonId), - PokemonIdNotInt.new, - ), - ); + final id = $.sync(Either.fromNullable( + int.tryParse(pokemonId), + PokemonIdNotInt.new, + ).provide()); if (id < Constants.minimumPokemonId && id > Constants.maximumPokemonId) { return $.sync(Effect.fail(const InvalidPokemonIdRange())); } final uri = Uri.parse(Constants.requestAPIUrl(id)); - final body = await $.async(Effect.tryCatch( - execute: () => client.get(uri), - onError: (_, __) => const GetPokemonRequestError(), - )); + final body = await $.async(client.get(uri).provideVoid()); final bodyJson = $.sync(Either.tryCatch( execute: () => json.decode(body), onError: (_, __) => const PokemonJsonDecodeError(), - )); + ).provide()); final bodyJsonMap = $.sync<Map<String, dynamic>>( - Either.safeCastStrict( + Either.safeCastStrict<PokemonError, Map<String, dynamic>, dynamic>( bodyJson, (value) => const PokemonJsonInvalidMap(), - ), + ).provide(), ); return $.sync(Effect.tryCatch( @@ -49,3 +61,14 @@ Effect<(HttpClient, JsonCodec), PokemonError, Pokemon> program( onError: (_, __) => const PokemonInvalidJsonModel(), )); }); + +void main() async { + await program("721") + .map((pokemon) => print(pokemon)) + .catchError( + (error) => Effect.function( + () => print("No pokemon: $error"), + ), + ) + .runFuture((Http(), JsonCodec())); +} diff --git a/examples/poke_api/lib/pokemon.dart b/examples/poke_api/lib/pokemon.dart index 34409b1..6008654 100644 --- a/examples/poke_api/lib/pokemon.dart +++ b/examples/poke_api/lib/pokemon.dart @@ -17,4 +17,9 @@ class Pokemon { height: json['height'] as int, weight: json['weight'] as int, ); + + @override + String toString() { + return "Pokemon(id:$id, name:$name, height:$height, weight:$weight)"; + } } diff --git a/examples/poke_api/pubspec.yaml b/examples/poke_api/pubspec.yaml index 423285d..1988048 100644 --- a/examples/poke_api/pubspec.yaml +++ b/examples/poke_api/pubspec.yaml @@ -10,6 +10,7 @@ environment: sdk: ">=3.3.0 <4.0.0" dependencies: + http: ^1.2.1 fpdart: path: ../../packages/fpdart diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 221c589..9e274fd 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -425,3 +425,8 @@ extension ProvideNever<L, R> on Effect<Never, L, R> { /// {@category execution} Future<Exit<L, R>> runFutureExitNoEnv() async => _unsafeRun(null); } + +extension ProvideVoid<L, R> on Effect<void, L, R> { + /// {@category execution} + Effect<V, L, R> provideVoid<V>() => provide((env) {}); +} diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index 5841e66..d3b1892 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -1,6 +1,6 @@ part of "effect.dart"; -sealed class Either<L, R> extends IEffect<Never, L, R> { +sealed class Either<L, R> extends IEffect<Null, L, R> { const Either(); /// If calling `predicate` with `r` returns `true`, then return `Right(r)`. diff --git a/packages/fpdart/lib/src/extension/future_or_extension.dart b/packages/fpdart/lib/src/extension/future_or_extension.dart index 578a99b..eade6c6 100644 --- a/packages/fpdart/lib/src/extension/future_or_extension.dart +++ b/packages/fpdart/lib/src/extension/future_or_extension.dart @@ -1,11 +1,8 @@ import 'dart:async'; extension FutureOrThenExtension<A> on FutureOr<A> { - FutureOr<B> then<B>(FutureOr<B> Function(A a) f) { - if (this is Future) { - return (this as Future<A>).then(f); - } - - return f(this as A); - } + FutureOr<B> then<B>(FutureOr<B> Function(A a) f) => switch (this) { + final Future<A> self => self.then(f), + final A self => f(self), + }; } From aa0d3a5eaa1c8567c99b1acada5db87951a6de6b Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 21 Mar 2024 18:50:55 +0900 Subject: [PATCH 39/91] working on types `Never` vs `void` --- examples/poke_api/lib/main.dart | 10 ++++++---- packages/fpdart/lib/src/effect.dart | 14 +++++++++----- packages/fpdart/lib/src/either.dart | 6 +++--- packages/fpdart/lib/src/option.dart | 12 ++++++------ 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index 43657e9..e2ac8fe 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -44,10 +44,12 @@ Effect<Env, PokemonError, Pokemon> program( final uri = Uri.parse(Constants.requestAPIUrl(id)); final body = await $.async(client.get(uri).provideVoid()); - final bodyJson = $.sync(Either.tryCatch( - execute: () => json.decode(body), - onError: (_, __) => const PokemonJsonDecodeError(), - ).provide()); + final bodyJson = $.sync( + Either.tryCatch( + execute: () => json.decode(body), + onError: (_, __) => const PokemonJsonDecodeError(), + ).provide(), + ); final bodyJsonMap = $.sync<Map<String, dynamic>>( Either.safeCastStrict<PokemonError, Map<String, dynamic>, dynamic>( diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 9e274fd..2285c4f 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -127,7 +127,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category constructors} - factory Effect.tryCatch({ + static Effect<void, L, R> tryCatch<L, R>({ required FutureOr<R> Function() execute, required L Function(Object error, StackTrace stackTrace) onError, FutureOr<dynamic> Function()? onCancel, @@ -143,20 +143,24 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category constructors} - factory Effect.fromNullable(R? value, L Function() onNull) => Effect._( + static Effect<void, L, R> fromNullable<L, R>(R? value, L Function() onNull) => + Effect._( (_) => value == null ? Left(Fail(onNull())) : Right(value), ); /// {@category constructors} - factory Effect.function(FutureOr<R> Function() f) => Effect._( + static Effect<void, void, R> function<R>(FutureOr<R> Function() f) => + Effect._( (_) => f().then(Right.new), ); /// {@category constructors} - factory Effect.fail(L value) => Effect._((_) => Left(Fail(value))); + static Effect<void, L, Never> fail<L>(L value) => + Effect._((_) => Left(Fail(value))); /// {@category constructors} - factory Effect.succeed(R value) => Effect._((_) => Right(value)); + static Effect<void, void, R> succeed<R>(R value) => + Effect._((_) => Right(value)); /// {@category constructors} static Effect<Never, Never, fpdart_unit.Unit> unit() => Effect._( diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index d3b1892..5dac202 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -1,6 +1,6 @@ part of "effect.dart"; -sealed class Either<L, R> extends IEffect<Null, L, R> { +sealed class Either<L, R> extends IEffect<void, L, R> { const Either(); /// If calling `predicate` with `r` returns `true`, then return `Right(r)`. @@ -67,7 +67,7 @@ final class Right<L, R> extends Either<L, R> { const Right(this.value); @override - Effect<Never, L, R> get asEffect => Effect.succeed(value); + Effect<void, L, R> get asEffect => Effect.succeed(value); Either<L, C> andThen<C>(Either<L, C> Function() then) => then(); @@ -115,7 +115,7 @@ final class Left<L, R> extends Either<L, R> { const Left(this.value); @override - Effect<Never, L, R> get asEffect => Effect.fail(value); + Effect<void, L, R> get asEffect => Effect.fail(value); Either<L, C> andThen<C>(Either<L, C> Function() then) => Left(value); diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index 9dcbfdf..1b05e52 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -1,6 +1,6 @@ part of "effect.dart"; -sealed class Option<R> extends IEffect<Never, Never, R> { +sealed class Option<R> extends IEffect<void, Never, R> { const Option(); factory Option.safeCast(dynamic value) => @@ -61,16 +61,16 @@ final class Some<R> extends Option<R> { const Some(this.value); @override - Effect<Never, Never, R> get asEffect => Effect.succeed(value); + Effect<void, Never, R> get asEffect => Effect.succeed(value).orDie; + + @override + Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect.succeed(value); Option<C> andThen<C>(Option<C> Function() then) => then(); @override Option<C> flatMap<C>(Option<C> Function(R r) f) => f(value); - @override - Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect.succeed(value); - @override R toNullable() => value; @@ -98,7 +98,7 @@ final class None extends Option<Never> { /// **This will always throw, don't use it!** // ignore: cast_from_null_always_fails - Effect<Never, Never, Never> get asEffect => Effect.fail(null as Never); + Effect<void, Never, Never> get asEffect => Effect.fail(null as Never); Option<C> andThen<C>(Option<C> Function() then) => this; From 4da33a1869afc3afa148f9eb10dc4bdba82f40df Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 22 Mar 2024 06:01:32 +0900 Subject: [PATCH 40/91] revert to use `Never` https://github.com/dart-lang/language/issues/3670#issuecomment-2011776343 --- examples/poke_api/lib/main.dart | 6 +++--- packages/fpdart/lib/src/effect.dart | 19 +++++-------------- packages/fpdart/lib/src/either.dart | 6 +++--- packages/fpdart/lib/src/option.dart | 6 +++--- 4 files changed, 14 insertions(+), 23 deletions(-) diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index e2ac8fe..59e29ca 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -7,12 +7,12 @@ import 'package:poke_api/pokemon.dart'; import 'package:poke_api/pokemon_error.dart'; abstract interface class HttpClient { - Effect<void, PokemonError, String> get(Uri uri); + Effect<Never, PokemonError, String> get(Uri uri); } class Http implements HttpClient { @override - Effect<void, PokemonError, String> get(Uri uri) => Effect.gen( + Effect<Never, PokemonError, String> get(Uri uri) => Effect.gen( ($) async { final response = await $.async(Effect.tryCatch( execute: () => http.get(uri), @@ -42,7 +42,7 @@ Effect<Env, PokemonError, Pokemon> program( } final uri = Uri.parse(Constants.requestAPIUrl(id)); - final body = await $.async(client.get(uri).provideVoid()); + final body = await $.async(client.get(uri)); final bodyJson = $.sync( Either.tryCatch( diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 2285c4f..221c589 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -127,7 +127,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category constructors} - static Effect<void, L, R> tryCatch<L, R>({ + factory Effect.tryCatch({ required FutureOr<R> Function() execute, required L Function(Object error, StackTrace stackTrace) onError, FutureOr<dynamic> Function()? onCancel, @@ -143,24 +143,20 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category constructors} - static Effect<void, L, R> fromNullable<L, R>(R? value, L Function() onNull) => - Effect._( + factory Effect.fromNullable(R? value, L Function() onNull) => Effect._( (_) => value == null ? Left(Fail(onNull())) : Right(value), ); /// {@category constructors} - static Effect<void, void, R> function<R>(FutureOr<R> Function() f) => - Effect._( + factory Effect.function(FutureOr<R> Function() f) => Effect._( (_) => f().then(Right.new), ); /// {@category constructors} - static Effect<void, L, Never> fail<L>(L value) => - Effect._((_) => Left(Fail(value))); + factory Effect.fail(L value) => Effect._((_) => Left(Fail(value))); /// {@category constructors} - static Effect<void, void, R> succeed<R>(R value) => - Effect._((_) => Right(value)); + factory Effect.succeed(R value) => Effect._((_) => Right(value)); /// {@category constructors} static Effect<Never, Never, fpdart_unit.Unit> unit() => Effect._( @@ -429,8 +425,3 @@ extension ProvideNever<L, R> on Effect<Never, L, R> { /// {@category execution} Future<Exit<L, R>> runFutureExitNoEnv() async => _unsafeRun(null); } - -extension ProvideVoid<L, R> on Effect<void, L, R> { - /// {@category execution} - Effect<V, L, R> provideVoid<V>() => provide((env) {}); -} diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index 5dac202..5841e66 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -1,6 +1,6 @@ part of "effect.dart"; -sealed class Either<L, R> extends IEffect<void, L, R> { +sealed class Either<L, R> extends IEffect<Never, L, R> { const Either(); /// If calling `predicate` with `r` returns `true`, then return `Right(r)`. @@ -67,7 +67,7 @@ final class Right<L, R> extends Either<L, R> { const Right(this.value); @override - Effect<void, L, R> get asEffect => Effect.succeed(value); + Effect<Never, L, R> get asEffect => Effect.succeed(value); Either<L, C> andThen<C>(Either<L, C> Function() then) => then(); @@ -115,7 +115,7 @@ final class Left<L, R> extends Either<L, R> { const Left(this.value); @override - Effect<void, L, R> get asEffect => Effect.fail(value); + Effect<Never, L, R> get asEffect => Effect.fail(value); Either<L, C> andThen<C>(Either<L, C> Function() then) => Left(value); diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index 1b05e52..72cefae 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -1,6 +1,6 @@ part of "effect.dart"; -sealed class Option<R> extends IEffect<void, Never, R> { +sealed class Option<R> extends IEffect<Never, Never, R> { const Option(); factory Option.safeCast(dynamic value) => @@ -61,7 +61,7 @@ final class Some<R> extends Option<R> { const Some(this.value); @override - Effect<void, Never, R> get asEffect => Effect.succeed(value).orDie; + Effect<Never, Never, R> get asEffect => Effect.succeed(value); @override Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect.succeed(value); @@ -98,7 +98,7 @@ final class None extends Option<Never> { /// **This will always throw, don't use it!** // ignore: cast_from_null_always_fails - Effect<void, Never, Never> get asEffect => Effect.fail(null as Never); + Effect<Never, Never, Never> get asEffect => Effect.fail(null as Never); Option<C> andThen<C>(Option<C> Function() then) => this; From 1ed678d765fd7f981881d23dbf5ffa46d001b5c7 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 22 Mar 2024 06:12:42 +0900 Subject: [PATCH 41/91] execute either as effect using `Never` --- examples/poke_api/lib/main.dart | 6 +++--- packages/fpdart/lib/src/either.dart | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index 59e29ca..ff69a95 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -35,7 +35,7 @@ Effect<Env, PokemonError, Pokemon> program( final id = $.sync(Either.fromNullable( int.tryParse(pokemonId), PokemonIdNotInt.new, - ).provide()); + )); if (id < Constants.minimumPokemonId && id > Constants.maximumPokemonId) { return $.sync(Effect.fail(const InvalidPokemonIdRange())); @@ -48,14 +48,14 @@ Effect<Env, PokemonError, Pokemon> program( Either.tryCatch( execute: () => json.decode(body), onError: (_, __) => const PokemonJsonDecodeError(), - ).provide(), + ), ); final bodyJsonMap = $.sync<Map<String, dynamic>>( Either.safeCastStrict<PokemonError, Map<String, dynamic>, dynamic>( bodyJson, (value) => const PokemonJsonInvalidMap(), - ).provide(), + ), ); return $.sync(Effect.tryCatch( diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index 5841e66..fc2a335 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -67,7 +67,7 @@ final class Right<L, R> extends Either<L, R> { const Right(this.value); @override - Effect<Never, L, R> get asEffect => Effect.succeed(value); + Effect<Never, L, R> get asEffect => Effect._((_) => Right(value)); Either<L, C> andThen<C>(Either<L, C> Function() then) => then(); @@ -115,7 +115,7 @@ final class Left<L, R> extends Either<L, R> { const Left(this.value); @override - Effect<Never, L, R> get asEffect => Effect.fail(value); + Effect<Never, L, R> get asEffect => Effect._((_) => Left(Fail(value))); Either<L, C> andThen<C>(Either<L, C> Function() then) => Left(value); From 7f3314edd4657ae44d30f2eb6a86f040355cc33a Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 22 Mar 2024 06:50:13 +0900 Subject: [PATCH 42/91] Use generic instead of `Never` --- examples/poke_api/lib/main.dart | 4 ++-- packages/fpdart/lib/src/option.dart | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index ff69a95..a670f1a 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -7,12 +7,12 @@ import 'package:poke_api/pokemon.dart'; import 'package:poke_api/pokemon_error.dart'; abstract interface class HttpClient { - Effect<Never, PokemonError, String> get(Uri uri); + Effect<E, PokemonError, String> get<E>(Uri uri); } class Http implements HttpClient { @override - Effect<Never, PokemonError, String> get(Uri uri) => Effect.gen( + Effect<E, PokemonError, String> get<E>(Uri uri) => Effect.gen( ($) async { final response = await $.async(Effect.tryCatch( execute: () => http.get(uri), diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index 72cefae..27e4a2f 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -61,7 +61,7 @@ final class Some<R> extends Option<R> { const Some(this.value); @override - Effect<Never, Never, R> get asEffect => Effect.succeed(value); + Effect<Never, Never, R> get asEffect => Effect._((_) => Right(value)); @override Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect.succeed(value); @@ -94,11 +94,9 @@ final class None extends Option<Never> { factory None() => _none; @override - @internal - - /// **This will always throw, don't use it!** - // ignore: cast_from_null_always_fails - Effect<Never, Never, Never> get asEffect => Effect.fail(null as Never); + Effect<Never, Never, Never> get asEffect => + // ignore: cast_from_null_always_fails + Effect._((_) => Left(Fail(null as Never))); Option<C> andThen<C>(Option<C> Function() then) => this; From a63aae1956299e0f13bceeb90eec1815ea4686a6 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 22 Mar 2024 07:35:14 +0900 Subject: [PATCH 43/91] `onError` for `gen` --- examples/poke_api/lib/main.dart | 6 ++++-- packages/fpdart/lib/src/effect.dart | 21 ++++++++++--------- packages/fpdart/lib/src/exit.dart | 5 +---- .../src/extension/future_or_extension.dart | 5 +++-- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index a670f1a..6ca46e0 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -65,12 +65,14 @@ Effect<Env, PokemonError, Pokemon> program( }); void main() async { - await program("721") + final exit = await program("72jj1") .map((pokemon) => print(pokemon)) .catchError( (error) => Effect.function( () => print("No pokemon: $error"), ), ) - .runFuture((Http(), JsonCodec())); + .runFutureExit((Http(), JsonCodec())); + + print(exit); } diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 221c589..af6f42f 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:fpdart/fpdart.dart'; import 'package:fpdart/src/extension/future_or_extension.dart'; import 'package:fpdart/src/extension/iterable_extension.dart'; -import 'package:meta/meta.dart'; import 'unit.dart' as fpdart_unit; @@ -15,7 +14,7 @@ typedef EffectGen<E, L> = ({ A Function<A>(IEffect<E, L, A>) sync, }); -final class _EffectThrow<L> { +final class _EffectThrow<L> implements Exception { final Cause<L> cause; const _EffectThrow(this.cause); @@ -29,7 +28,7 @@ EffectGen<E, L> _effectGen<E, L>(E? env) => ( async: <A>(effect) => Future.sync( () => effect.asEffect._unsafeRun(env).then( (exit) => switch (exit) { - Left(value: final cause) => throw _EffectThrow(cause), + Left(value: final cause) => throw _EffectThrow<L>(cause), Right(value: final value) => value, }, ), @@ -43,7 +42,7 @@ EffectGen<E, L> _effectGen<E, L>(E? env) => ( } return switch (run) { - Left(value: final cause) => throw _EffectThrow(cause), + Left(value: final cause) => throw _EffectThrow<L>(cause), Right(value: final value) => value, }; }, @@ -115,14 +114,16 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Future<Exit<L, R>> runFutureExit(E env) async => _unsafeRun(env); /// {@category constructors} - // ignore: non_constant_identifier_names factory Effect.gen(DoFunctionEffect<E, L, R> f) => Effect<E, L, R>._( (env) { - try { - return f(_effectGen<E, L>(env)).then(Right.new); - } on _EffectThrow<L> catch (err) { - return Left(err.cause); - } + return f(_effectGen<E, L>(env)).then( + Right.new, + onError: (dynamic error, dynamic stackTrack) { + if (error is _EffectThrow<L>) { + return Left<Cause<L>, R>(error.cause); + } + }, + ); }, ); diff --git a/packages/fpdart/lib/src/exit.dart b/packages/fpdart/lib/src/exit.dart index 0430eb9..aa6b3df 100644 --- a/packages/fpdart/lib/src/exit.dart +++ b/packages/fpdart/lib/src/exit.dart @@ -2,11 +2,8 @@ import 'effect.dart'; typedef Exit<L, R> = Either<Cause<L>, R>; -sealed class Cause<L> { +sealed class Cause<L> implements Error { const Cause(); - - StackTrace? get stackTrace; - Cause<L> withTrace(StackTrace stack); } diff --git a/packages/fpdart/lib/src/extension/future_or_extension.dart b/packages/fpdart/lib/src/extension/future_or_extension.dart index eade6c6..d3fac8f 100644 --- a/packages/fpdart/lib/src/extension/future_or_extension.dart +++ b/packages/fpdart/lib/src/extension/future_or_extension.dart @@ -1,8 +1,9 @@ import 'dart:async'; extension FutureOrThenExtension<A> on FutureOr<A> { - FutureOr<B> then<B>(FutureOr<B> Function(A a) f) => switch (this) { - final Future<A> self => self.then(f), + FutureOr<B> then<B>(FutureOr<B> Function(A a) f, {Function? onError}) => + switch (this) { + final Future<A> self => self.then(f, onError: onError), final A self => f(self), }; } From e82dba66287a8b4e48acacaee10a1bac50f6db0a Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sat, 23 Mar 2024 05:31:43 +0900 Subject: [PATCH 44/91] `catchError` and `catchCause` --- examples/poke_api/lib/main.dart | 6 ++--- packages/fpdart/lib/src/effect.dart | 38 +++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index 6ca46e0..3c24ebf 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -37,7 +37,7 @@ Effect<Env, PokemonError, Pokemon> program( PokemonIdNotInt.new, )); - if (id < Constants.minimumPokemonId && id > Constants.maximumPokemonId) { + if (id < Constants.minimumPokemonId || id > Constants.maximumPokemonId) { return $.sync(Effect.fail(const InvalidPokemonIdRange())); } @@ -65,9 +65,9 @@ Effect<Env, PokemonError, Pokemon> program( }); void main() async { - final exit = await program("72jj1") + final exit = await program("9722") .map((pokemon) => print(pokemon)) - .catchError( + .catchError<void>( (error) => Effect.function( () => print("No pokemon: $error"), ), diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index af6f42f..076fcee 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -103,15 +103,29 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category execution} Future<R> runFuture(E env) async { - final result = await _unsafeRun(env); - return switch (result) { + final result = _unsafeRun(env); + if (result is! Future) { + print( + "You can use runSync instead of runFuture since the Effect is synchronous", + ); + } + + return switch (await result) { Left(value: final cause) => throw cause, Right(value: final value) => value, }; } /// {@category execution} - Future<Exit<L, R>> runFutureExit(E env) async => _unsafeRun(env); + Future<Exit<L, R>> runFutureExit(E env) async { + final result = _unsafeRun(env); + if (result is! Future) { + print( + "You can use runSyncExit instead of runFutureExit since the Effect is synchronous", + ); + } + return result; + } /// {@category constructors} factory Effect.gen(DoFunctionEffect<E, L, R> f) => Effect<E, L, R>._( @@ -366,8 +380,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category error_handling} - Effect<E, Never, R> catchError( - Effect<E, Never, R> Function(L error) f, + Effect<E, C, R> catchError<C>( + Effect<E, C, R> Function(L error) f, ) => Effect._( (env) => _unsafeRun(env).then( @@ -382,6 +396,20 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { }, ), ); + + /// {@category error_handling} + Effect<E, C, R> catchCause<C>( + Effect<E, C, R> Function(Cause<L> cause) f, + ) => + Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => f(cause), + Right(value: final value) => Effect<E, C, R>.succeed(value), + } + ._unsafeRun(env), + ), + ); } extension ProvideNever<L, R> on Effect<Never, L, R> { From 05036f1d6c27925271d3da42a97ff00ecb5b56c9 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sat, 23 Mar 2024 05:57:25 +0900 Subject: [PATCH 45/91] factory constructors --- examples/fpdart_http/lib/main.dart | 2 +- examples/poke_api/lib/main.dart | 2 +- packages/fpdart/analysis_options.yaml | 1 + packages/fpdart/lib/src/effect.dart | 46 ++++++++++++------- packages/fpdart/pubspec.yaml | 1 - .../src/effect/effect_collecting_test.dart | 2 +- .../src/effect/effect_constructors_test.dart | 4 +- .../src/effect/effect_sequencing_test.dart | 2 +- 8 files changed, 36 insertions(+), 24 deletions(-) diff --git a/examples/fpdart_http/lib/main.dart b/examples/fpdart_http/lib/main.dart index ce8caf5..6542ac4 100644 --- a/examples/fpdart_http/lib/main.dart +++ b/examples/fpdart_http/lib/main.dart @@ -8,7 +8,7 @@ void main() async { Uri.https("pokeapi.co", "/api/v2/pokemon/10"), ) .tap( - (response) => Effect.function( + (response) => Effect.functionSucceed( () => print(response.body), ), ) diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index 3c24ebf..55e4f2c 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -68,7 +68,7 @@ void main() async { final exit = await program("9722") .map((pokemon) => print(pokemon)) .catchError<void>( - (error) => Effect.function( + (error) => Effect.functionSucceed( () => print("No pokemon: $error"), ), ) diff --git a/packages/fpdart/analysis_options.yaml b/packages/fpdart/analysis_options.yaml index 4701dcd..d9d369f 100644 --- a/packages/fpdart/analysis_options.yaml +++ b/packages/fpdart/analysis_options.yaml @@ -3,6 +3,7 @@ include: package:lints/recommended.yaml linter: rules: annotate_overrides: true + prefer_const_constructors: true analyzer: language: diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 076fcee..1b692bd 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -62,14 +62,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// /// In practice a user of the library should never be allowed to pass `null` as [E]. final FutureOr<Exit<L, R>> Function(E? env) _unsafeRun; - final StackTrace? stackTrace; - static bool debugTracing = false; - - const Effect._( - this._unsafeRun, { - this.stackTrace, - }); + const Effect._(this._unsafeRun); @override Effect<E, L, R> get asEffect => this; @@ -83,7 +77,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { R runSync(E env) { final result = _unsafeRun(env); if (result is Future) { - throw Die.current(result, stackTrace); + throw Die.current(result); } return switch (result) { @@ -163,7 +157,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category constructors} - factory Effect.function(FutureOr<R> Function() f) => Effect._( + factory Effect.functionFail(FutureOr<Cause<L>> Function() f) => Effect._( + (_) => f().then(Left.new), + ); + + /// {@category constructors} + factory Effect.functionSucceed(FutureOr<R> Function() f) => Effect._( (_) => f().then(Right.new), ); @@ -174,9 +173,9 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { factory Effect.succeed(R value) => Effect._((_) => Right(value)); /// {@category constructors} - static Effect<Never, Never, fpdart_unit.Unit> unit() => Effect._( - (_) => Right(fpdart_unit.unit), - ); + static Effect<Never, Never, fpdart_unit.Unit> unit = Effect._( + (_) => const Right(fpdart_unit.unit), + ); /// {@category collecting} static Effect<E, L, Iterable<R>> forEach<E, L, R, A>( @@ -186,13 +185,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect._( (env) { if (iterable.isEmpty) { - return Right([]); + return const Right([]); } return iterable .mapWithIndex(f) .fold<Effect<E, L, Iterable<R>>>( - Effect.succeed(Iterable.empty()), + Effect.succeed(const Iterable.empty()), (acc, effect) => acc.zipWith( effect, (list, r) => list.append(r), @@ -355,7 +354,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, Never, R> get orDie => Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { - Left(value: final cause) => Left(Die.current(cause, stackTrace)), + Left(value: final cause) => Left(Die.current(cause)), Right(value: final value) => Effect<E, Never, R>.succeed(value)._unsafeRun(env), }, @@ -424,7 +423,7 @@ extension ProvideNever<L, R> on Effect<Never, L, R> { R runSyncNoEnv() { final result = _unsafeRun(null); if (result is Future) { - throw Die.current(result, stackTrace); + throw Die.current(result); } return switch (result) { @@ -445,6 +444,11 @@ extension ProvideNever<L, R> on Effect<Never, L, R> { /// {@category execution} Future<R> runFutureNoEnv() async { final result = await _unsafeRun(null); + if (result is! Future) { + print( + "You can use runSync instead of runFuture since the Effect is synchronous", + ); + } return switch (result) { Left(value: final cause) => throw cause, Right(value: final value) => value, @@ -452,5 +456,13 @@ extension ProvideNever<L, R> on Effect<Never, L, R> { } /// {@category execution} - Future<Exit<L, R>> runFutureExitNoEnv() async => _unsafeRun(null); + Future<Exit<L, R>> runFutureExitNoEnv() async { + final result = _unsafeRun(null); + if (result is! Future) { + print( + "You can use runSync instead of runFuture since the Effect is synchronous", + ); + } + return result; + } } diff --git a/packages/fpdart/pubspec.yaml b/packages/fpdart/pubspec.yaml index 959e5cd..b952df4 100644 --- a/packages/fpdart/pubspec.yaml +++ b/packages/fpdart/pubspec.yaml @@ -18,7 +18,6 @@ dependencies: dev_dependencies: lints: ^2.0.1 test: ^1.23.1 - glados: ^1.1.6 collection: ^1.17.2 screenshots: diff --git a/packages/fpdart/test/src/effect/effect_collecting_test.dart b/packages/fpdart/test/src/effect/effect_collecting_test.dart index dcc2497..ea12633 100644 --- a/packages/fpdart/test/src/effect/effect_collecting_test.dart +++ b/packages/fpdart/test/src/effect/effect_collecting_test.dart @@ -20,7 +20,7 @@ void main() { final main = Effect.all<dynamic, String, int>([ Effect.succeed(10), Effect.fail("10"), - Effect.function(() => mutable += 1), + Effect.functionSucceed(() => mutable += 1), Effect.fail("0"), ]); final result = main.flip.runSync(null); diff --git a/packages/fpdart/test/src/effect/effect_constructors_test.dart b/packages/fpdart/test/src/effect/effect_constructors_test.dart index 646c60b..58da061 100644 --- a/packages/fpdart/test/src/effect/effect_constructors_test.dart +++ b/packages/fpdart/test/src/effect/effect_constructors_test.dart @@ -72,7 +72,7 @@ void main() { test('async succeed', () async { final main = Effect.gen(($) async { final value = - await $.async(Effect.function(() => Future.value(10))); + await $.async(Effect.functionSucceed(() => Future.value(10))); return value; }); final result = await main.runFutureNoEnv(); @@ -81,7 +81,7 @@ void main() { test('fail when running async as sync', () async { final main = Effect.gen(($) { - final value = $.sync(Effect.function( + final value = $.sync(Effect.functionSucceed( () async => Future.value(10), )); return value; diff --git a/packages/fpdart/test/src/effect/effect_sequencing_test.dart b/packages/fpdart/test/src/effect/effect_sequencing_test.dart index 3e1db93..0480bd1 100644 --- a/packages/fpdart/test/src/effect/effect_sequencing_test.dart +++ b/packages/fpdart/test/src/effect/effect_sequencing_test.dart @@ -20,7 +20,7 @@ void main() { test('tap', () { var mutable = 0; final main = Effect.succeed(10).tap( - (_) => Effect.function(() { + (_) => Effect.functionSucceed(() { mutable += 1; }), ); From a44fed8e1953dbc24d43b9f3757f16a8b104f731 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sat, 23 Mar 2024 06:38:00 +0900 Subject: [PATCH 46/91] running `Effect` --- packages/fpdart/lib/src/effect.dart | 149 +++++++++++++++------------- 1 file changed, 81 insertions(+), 68 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 1b692bd..ee10bb2 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -37,7 +37,9 @@ EffectGen<E, L> _effectGen<E, L>(E? env) => ( final run = effect.asEffect._unsafeRun(env); if (run is Future) { throw _EffectThrow<L>( - Die.current("Cannot execute a Future using sync"), + Die.current( + Exception("gen.sync cannot execute async Effect"), + ), ); } @@ -75,50 +77,77 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category execution} R runSync(E env) { - final result = _unsafeRun(env); - if (result is Future) { - throw Die.current(result); + try { + final result = _unsafeRun(env); + if (result is Future) { + throw Die.current( + Exception("runSync cannot execute async Effect"), + ); + } + + return switch (result) { + Left(value: final cause) => throw cause, + Right(value: final value) => value, + }; + } on Cause<L> { + rethrow; + } catch (error, stackTrace) { + throw Die(error, stackTrace); } - - return switch (result) { - Left(value: final cause) => throw cause, - Right(value: final value) => value, - }; } /// {@category execution} Exit<L, R> runSyncExit(E env) { - final result = _unsafeRun(env); - if (result is Future) { - return Left(Die.current("")); + try { + final result = _unsafeRun(env); + if (result is Future) { + return Left(Die.current( + Exception("runSyncExit cannot execute async Effect"), + )); + } + return result; + } on Cause<L> catch (cause) { + return Left(cause); + } catch (error, stackTrace) { + return Left(Die(error, stackTrace)); } - return result; } /// {@category execution} Future<R> runFuture(E env) async { - final result = _unsafeRun(env); - if (result is! Future) { - print( - "You can use runSync instead of runFuture since the Effect is synchronous", - ); + try { + final result = _unsafeRun(env); + if (result is! Future) { + return switch (result) { + Left(value: final cause) => throw cause, + Right(value: final value) => value, + }; + } + + return switch (await result) { + Left(value: final cause) => throw cause, + Right(value: final value) => value, + }; + } on Cause<L> { + rethrow; + } catch (error, stackTrace) { + throw Die(error, stackTrace); } - - return switch (await result) { - Left(value: final cause) => throw cause, - Right(value: final value) => value, - }; } /// {@category execution} Future<Exit<L, R>> runFutureExit(E env) async { - final result = _unsafeRun(env); - if (result is! Future) { - print( - "You can use runSyncExit instead of runFutureExit since the Effect is synchronous", - ); + try { + final result = _unsafeRun(env); + if (result is! Future) { + return result; + } + return result; + } on Cause<L> catch (cause) { + return Left(cause); + } catch (error, stackTrace) { + return Left(Die(error, stackTrace)); } - return result; } /// {@category constructors} @@ -172,6 +201,17 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category constructors} factory Effect.succeed(R value) => Effect._((_) => Right(value)); + /// {@category constructors} + static Effect<E, Never, Never> die<E>(dynamic defect) => Effect._( + (_) => Left(Die.current(defect)), + ); + + /// {@category constructors} + static Effect<E, Never, Never> functionDie<E>(dynamic Function() run) => + Effect._( + (_) => Left(Die.current(run())), + ); + /// {@category constructors} static Effect<Never, Never, fpdart_unit.Unit> unit = Effect._( (_) => const Right(fpdart_unit.unit), @@ -420,49 +460,22 @@ extension ProvideNever<L, R> on Effect<Never, L, R> { ); /// {@category execution} - R runSyncNoEnv() { - final result = _unsafeRun(null); - if (result is Future) { - throw Die.current(result); - } - - return switch (result) { - Left(value: final cause) => throw cause, - Right(value: final value) => value, - }; - } + R runSyncNoEnv() => Effect<void, L, R>._( + (_) => _unsafeRun(null), + ).runSync(null); /// {@category execution} - Exit<L, R> runSyncExitNoEnv() { - final result = _unsafeRun(null); - if (result is Future) { - return Left(Die.current("")); - } - return result; - } + Exit<L, R> runSyncExitNoEnv() => Effect<void, L, R>._( + (_) => _unsafeRun(null), + ).runSyncExit(null); /// {@category execution} - Future<R> runFutureNoEnv() async { - final result = await _unsafeRun(null); - if (result is! Future) { - print( - "You can use runSync instead of runFuture since the Effect is synchronous", - ); - } - return switch (result) { - Left(value: final cause) => throw cause, - Right(value: final value) => value, - }; - } + Future<R> runFutureNoEnv() async => Effect<void, L, R>._( + (_) => _unsafeRun(null), + ).runFuture(null); /// {@category execution} - Future<Exit<L, R>> runFutureExitNoEnv() async { - final result = _unsafeRun(null); - if (result is! Future) { - print( - "You can use runSync instead of runFuture since the Effect is synchronous", - ); - } - return result; - } + Future<Exit<L, R>> runFutureExitNoEnv() async => Effect<void, L, R>._( + (_) => _unsafeRun(null), + ).runFutureExit(null); } From 0f91eeb6b93d9c3062c931f03bbc856fa9a7a5b0 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sat, 23 Mar 2024 06:39:31 +0900 Subject: [PATCH 47/91] clean up `Cause` --- packages/fpdart/lib/src/effect.dart | 7 ------- packages/fpdart/lib/src/exit.dart | 25 ------------------------- 2 files changed, 32 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index ee10bb2..7a69f5b 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -302,7 +302,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Fail<L>(error: final error) => Right(error), - Empty() => Left(cause), Die() => Left(cause), }, Right(value: final value) => Left(Fail(value)), @@ -319,7 +318,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Fail<L>(error: final error) => Left(Fail(f(error))), - Empty() => Left(cause), Die() => Left(cause), }, Right(value: final value) => Right(value), @@ -334,7 +332,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Fail<L>(error: final error) => Left(Fail(fl(error))), - Empty() => Left(cause), Die() => Left(cause), }, Right(value: final value) => Right(fr(value)), @@ -364,7 +361,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Fail<L>(error: final error) => f(error)._unsafeRun(env).then( (_) => Left(Fail(error)), ), - Empty() => Left(cause), Die() => Left(cause), }, Right(value: final value) => Right(value), @@ -381,7 +377,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Fail<L>(error: final error) => orElse(error)._unsafeRun(env), - Empty() => Left(cause), Die() => Left(cause), }, Right(value: final value) => @@ -409,7 +404,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Left(value: final cause) => switch (cause) { Fail<L>(error: final error) => Left(Die.current(onError(error))), - Empty() => Left(cause), Die() => Left(cause), }, Right(value: final value) => @@ -427,7 +421,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Fail<L>(error: final error) => f(error)._unsafeRun(env), - Empty() => Left(cause), Die() => Left(cause), }, Right(value: final value) => diff --git a/packages/fpdart/lib/src/exit.dart b/packages/fpdart/lib/src/exit.dart index aa6b3df..de225cf 100644 --- a/packages/fpdart/lib/src/exit.dart +++ b/packages/fpdart/lib/src/exit.dart @@ -4,23 +4,6 @@ typedef Exit<L, R> = Either<Cause<L>, R>; sealed class Cause<L> implements Error { const Cause(); - Cause<L> withTrace(StackTrace stack); -} - -/// Represents a lack of errors -final class Empty extends Cause<Never> { - @override - final StackTrace? stackTrace; - - const Empty([this.stackTrace]); - - @override - Empty withTrace(StackTrace stack) => stackTrace == null ? Empty(stack) : this; - - @override - String toString() { - return "Cause.Empty()"; - } } /// Failed as a result of a defect (unexpected error) @@ -36,10 +19,6 @@ final class Die extends Cause<Never> { factory Die.current(dynamic error, [StackTrace? stackTrace]) => Die(error, StackTrace.current, stackTrace); - @override - Die withTrace(StackTrace stack) => - stackTrace == null ? Die(error, defectStackTrace, stack) : this; - @override bool operator ==(Object other) => (other is Fail) && other.error == error; @@ -61,10 +40,6 @@ final class Fail<L> extends Cause<L> { const Fail(this.error, [this.stackTrace]); - @override - Fail<L> withTrace(StackTrace stack) => - stackTrace == null ? Fail(error, stack) : this; - @override bool operator ==(Object other) => (other is Fail) && other.error == error; From f7cdc3d2ea4578a83238083a2c396b2e2587a881 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sat, 23 Mar 2024 07:29:21 +0900 Subject: [PATCH 48/91] fix fail in `Effect.gen` --- packages/fpdart/lib/src/effect.dart | 24 +++++++++++-------- .../src/effect/effect_collecting_test.dart | 2 +- .../src/effect/effect_constructors_test.dart | 8 +++---- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 7a69f5b..f3ba871 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -60,7 +60,7 @@ abstract interface class IEffect<E, L, R> { } final class Effect<E, L, R> extends IEffect<E, L, R> { - /// `E?` is optional to allow [Never] to work (`provideNever`). + /// `E?` is optional to allow [Never] to work. /// /// In practice a user of the library should never be allowed to pass `null` as [E]. final FutureOr<Exit<L, R>> Function(E? env) _unsafeRun; @@ -153,14 +153,18 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category constructors} factory Effect.gen(DoFunctionEffect<E, L, R> f) => Effect<E, L, R>._( (env) { - return f(_effectGen<E, L>(env)).then( - Right.new, - onError: (dynamic error, dynamic stackTrack) { - if (error is _EffectThrow<L>) { - return Left<Cause<L>, R>(error.cause); - } - }, - ); + try { + return f(_effectGen<E, L>(env)).then( + Right.new, + onError: (dynamic error) { + if (error is _EffectThrow<L>) { + return Left<Cause<L>, R>(error.cause); + } + }, + ); + } on _EffectThrow<L> catch (genError) { + return Left(genError.cause); + } }, ); @@ -297,7 +301,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category mapping} - Effect<E, R, L> get flip => Effect._( + Effect<E, R, L> flip() => Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { diff --git a/packages/fpdart/test/src/effect/effect_collecting_test.dart b/packages/fpdart/test/src/effect/effect_collecting_test.dart index ea12633..a15c0f4 100644 --- a/packages/fpdart/test/src/effect/effect_collecting_test.dart +++ b/packages/fpdart/test/src/effect/effect_collecting_test.dart @@ -23,7 +23,7 @@ void main() { Effect.functionSucceed(() => mutable += 1), Effect.fail("0"), ]); - final result = main.flip.runSync(null); + final result = main.flip().runSync(null); expect(mutable, 0); expect(result, "10"); }); diff --git a/packages/fpdart/test/src/effect/effect_constructors_test.dart b/packages/fpdart/test/src/effect/effect_constructors_test.dart index 58da061..5f54418 100644 --- a/packages/fpdart/test/src/effect/effect_constructors_test.dart +++ b/packages/fpdart/test/src/effect/effect_constructors_test.dart @@ -13,7 +13,7 @@ void main() { test('fail', () { final main = Effect.fail(10); - final result = main.flip.runSync(null); + final result = main.flip().runSync(null); expect(result, 10); }); @@ -62,11 +62,11 @@ void main() { test('sync fail', () { final main = Effect<Never, String, int>.gen(($) { - final value = $.sync(Effect.fail("10")); + final value = $.sync(Effect.fail("abc")); return value; }); - final result = main.flip.runSyncNoEnv(); - expect(result, "10"); + final result = main.flip().runSyncNoEnv(); + expect(result, "abc"); }); test('async succeed', () async { From 62a5d01fa43c4e26a044d23f24b024b9a39304d3 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sat, 23 Mar 2024 17:19:06 +0900 Subject: [PATCH 49/91] conversions and folding --- packages/fpdart/lib/src/effect.dart | 114 +++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 4 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index f3ba871..e2a9079 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -185,7 +185,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category constructors} - factory Effect.fromNullable(R? value, L Function() onNull) => Effect._( + factory Effect.fromNullable(R? value, {required L Function() onNull}) => + Effect._( (_) => value == null ? Left(Fail(onNull())) : Right(value), ); @@ -217,9 +218,9 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category constructors} - static Effect<Never, Never, fpdart_unit.Unit> unit = Effect._( - (_) => const Right(fpdart_unit.unit), - ); + static Effect<E, L, fpdart_unit.Unit> unit<E, L>() => Effect._( + (_) => const Right(fpdart_unit.unit), + ); /// {@category collecting} static Effect<E, L, Iterable<R>> forEach<E, L, R, A>( @@ -300,6 +301,101 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); + /// {@category conversions} + Effect<E, Never, Either<L, R>> either() => Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => switch (cause) { + Fail<L>(error: final error) => Right(Left(error)), + Die() => Left(cause), + }, + Right(value: final value) => Right(Right(value)), + }, + ), + ); + + /// {@category conversions} + Effect<E, Never, Option<R>> option() => Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => switch (cause) { + Fail<L>() => Right(None()), + Die() => Left(cause), + }, + Right(value: final value) => Right(Some(value)), + }, + ), + ); + + /// {@category conversions} + Effect<E, Never, Exit<L, R>> exit() => Effect._( + (env) => _unsafeRun(env).then( + (exit) => Right(exit), + ), + ); + + /// {@category folding} + Effect<E, Never, C> match<C>({ + required C Function(L l) onFailure, + required C Function(R r) onSuccess, + }) => + Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => switch (cause) { + Fail<L>(error: final error) => Right(onFailure(error)), + Die() => Left(cause), + }, + Right(value: final value) => Right(onSuccess(value)), + }, + ), + ); + + /// {@category folding} + Effect<E, Never, C> matchCause<C>({ + required C Function(Cause<L> l) onFailure, + required C Function(R r) onSuccess, + }) => + Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => Right(onFailure(cause)), + Right(value: final value) => Right(onSuccess(value)), + }, + ), + ); + + /// {@category folding} + Effect<E, C, D> matchEffect<C, D>({ + required Effect<E, C, D> Function(L l) onFailure, + required Effect<E, C, D> Function(R r) onSuccess, + }) => + Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => switch (cause) { + Fail<L>(error: final error) => onFailure(error)._unsafeRun(env), + Die() => Left(cause), + }, + Right(value: final value) => onSuccess(value)._unsafeRun(env), + }, + ), + ); + + /// {@category folding} + Effect<E, C, D> matchCauseEffect<C, D>({ + required Effect<E, C, D> Function(Cause<L> l) onFailure, + required Effect<E, C, D> Function(R r) onSuccess, + }) => + Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => onFailure(cause)._unsafeRun(env), + Right(value: final value) => onSuccess(value)._unsafeRun(env), + }, + ), + ); + /// {@category mapping} Effect<E, R, L> flip() => Effect._( (env) => _unsafeRun(env).then( @@ -329,6 +425,16 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); + /// {@category mapping} + Effect<E, C, R> mapErrorCause<C>(C Function(Cause<L> l) f) => Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => Left(Fail(f(cause))), + Right(value: final value) => Right(value), + }, + ), + ); + /// {@category mapping} Effect<E, C, D> mapBoth<C, D>(C Function(L l) fl, D Function(R r) fr) => Effect._( From ab71d1eb68dc0b881e2925294410dd9fddb26618 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sat, 23 Mar 2024 17:20:48 +0900 Subject: [PATCH 50/91] imports --- packages/fpdart/lib/src/effect.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index e2a9079..5bf4de3 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -1,9 +1,8 @@ import 'dart:async'; -import 'package:fpdart/fpdart.dart'; -import 'package:fpdart/src/extension/future_or_extension.dart'; -import 'package:fpdart/src/extension/iterable_extension.dart'; - +import './extension/future_or_extension.dart'; +import './extension/iterable_extension.dart'; +import 'exit.dart'; import 'unit.dart' as fpdart_unit; part 'either.dart'; From e27399e239da6861abea6ca330a4fbd9cb89c7d0 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sat, 23 Mar 2024 17:43:29 +0900 Subject: [PATCH 51/91] prepare v2 dev pre release --- packages/fpdart/CHANGELOG.md | 4 ++ packages/fpdart/MIGRATION.md | 66 ------------------ packages/fpdart/README.md | 20 +++--- packages/fpdart/example/main.dart | 15 +++- packages/fpdart/example/screenshot_fpdart.png | Bin 153192 -> 182066 bytes packages/fpdart/lib/src/effect.dart | 2 + .../lib/src/extension/curry_extension.dart | 14 ++-- packages/fpdart/pubspec.yaml | 6 +- 8 files changed, 42 insertions(+), 85 deletions(-) delete mode 100644 packages/fpdart/MIGRATION.md diff --git a/packages/fpdart/CHANGELOG.md b/packages/fpdart/CHANGELOG.md index 73c07b1..faa383c 100644 --- a/packages/fpdart/CHANGELOG.md +++ b/packages/fpdart/CHANGELOG.md @@ -1,3 +1,7 @@ +## v2.0.0-dev.1 - 23 March 2024 +- Initial preview release of `fpdart` v2 + - Refactoring to use `Effect` class + ## v1.1.0 - 13 August 2023 - Improved performance of some iterable based functions in `Iterable` and `Map` extension (thanks to [lrhn](https://github.com/lrhn) 🎉) diff --git a/packages/fpdart/MIGRATION.md b/packages/fpdart/MIGRATION.md deleted file mode 100644 index 034e9bc..0000000 --- a/packages/fpdart/MIGRATION.md +++ /dev/null @@ -1,66 +0,0 @@ -## From `fpdart` v1 to v2 - -### Problems with v1 -Too many classes (`IO`, `IOOption`, `IOEither`, `Task`, `TaskOption`, `TaskEither`, `Reader`, `State`, `ReaderTaskEither`): -- Similar implementation with different generic parameters = **A lot of duplicated code** (both core and tests) -```dart -/// [IO] 👇 -abstract final class _IOHKT {} - -final class IO<A> extends HKT<_IOHKT, A> - with Functor<_IOHKT, A>, Applicative<_IOHKT, A>, Monad<_IOHKT, A> { - final A Function() _run; -} - -/// [Task] 👇 -abstract final class _TaskHKT {} - -final class Task<A> extends HKT<_TaskHKT, A> - with Functor<_TaskHKT, A>, Applicative<_TaskHKT, A>, Monad<_TaskHKT, A> { - final Future<A> Function() _run; /// 👈 Difference: [Future] here -} -``` -- Code duplication = Hard to maintain, more lines of code, more code to read (and understand) for contributors -- Requires conversion between classes (`from*`, `to*`, e.g. `toTask`, `toTaskEither`) -- Requires having a different `Do` constructor for each class, making the do notation harder to use -- Hard to understand for newcomers, hard to reason with and explain for veterans (and verbose) -- More complex code, less contributors - -**Too much jargon**: methods and classes are using terms from pure functional programming (math), less clear and hard to understand (e.g. `pure`, `Reader`, `chainFirst`, `traverse`, `Endo`). - -Typeclasses do not work well with dart and they cause a lot of overhead to maintain and understand. In fact, they are not necessary to implement the core of fpdart (**they can be removed** 💁🏼♂️). - -Too many "utility functions" that may be considered outside of the scope of fpdart (e.g. `predicate_extension.dart`). - -### fpdart v2 solution: `Effect` -A single `Effect` class that contains the API of all other classes in v1 (similar to `ReaderTaskEither`). - -All Effect-classes derive from the same interface `IEffect`: - -```dart -abstract interface class IEffect<E, L, R> { - const IEffect(); - Effect<E, L, R> get asEffect; -} -``` - -Benefits: -- A lot less code: easier to maintain, contribute, test, understand (a single `effect.dart`) -- No need of conversion methods (a lot less code) -- A single Do notation (implemented as a factory constructor `factory Effect.gen`): the do notation also includes `Option` and `Either` (since both are extend `IEffect`) -- No more jargon: easy to understand method names instead of fp jargon (e.g. `succeed` instead of `pure`) -- Removed all typeclasses and unnecessary utility methods -- Easier to explain and understand (focus on learning a single `Effect` and how it works) -- **Smaller API that allows all the same functionalities as before** -- More resources to focus on better documentation, tests, and examples - -> **Important**: `Effect` comes from the [`effect`](https://www.effect.website/) library (typescript), which itself was inspired from [`ZIO`](https://zio.dev/). -> -> The `Effect` class and methods in `fpdart` are based on `effect` from typescript (similar API and methods names). -> -> Huge thanks also to [Tim Smart](https://github.com/tim-smart) for his initial [`zio.dart`](https://github.com/tim-smart/elemental/blob/main/packages/elemental/lib/src/zio.dart) implementation. - -### Downsides -- ⚠️ **Huge breaking change** ⚠️ -- Nearly all tests need to be rewritten -- Documentation and examples to redo completely \ No newline at end of file diff --git a/packages/fpdart/README.md b/packages/fpdart/README.md index 7ed80a9..ae3ca5b 100644 --- a/packages/fpdart/README.md +++ b/packages/fpdart/README.md @@ -6,11 +6,11 @@ <p align="center"> -<strong>Functional programming in Dart and Flutter</strong> +<strong>Functional Effect System in Dart and Flutter (v2)</strong> </p> <p align="center"> -All the main functional programming types and patterns <strong>fully documented</strong>, tested, and with examples +Functional Effect System <strong>fully documented</strong>, tested, and with examples, to write complex type-safe dart applications </p> <h3 align="center"> @@ -35,11 +35,13 @@ All the main functional programming types and patterns <strong>fully documented< </a> </p> +> 🏗️ Pre-release version of `fpdart v2` + ## Introduction -> **fpdart is fully documented. You do not need to have any previous experience with functional programming to start using `fpdart`. Give it a try!** +> **fpdart is fully documented. You do not need to have any previous experience with functional effect systems to start using `fpdart`. Give it a try!** -fpdart is inspired by [fp-ts](https://gcanti.github.io/fp-ts/), [cats](https://typelevel.org/cats/typeclasses.html#type-classes-in-cats), and [dartz](https://github.com/spebbe/dartz). +fpdart is inspired by [effect](https://www.effect.website/) and [dartz](https://github.com/spebbe/dartz). > Follow my [**Twitter**](https://twitter.com/SandroMaglione) for updates, or [subscribe to the newsletter](https://www.sandromaglione.com/newsletter) @@ -120,10 +122,8 @@ Interested in what `fpdart` is and how it came to be? ## 💻 Installation -```yaml -# pubspec.yaml -dependencies: - fpdart: ^1.1.0 +```shell +dart pub add fpdart:'^2.0.0' ``` ## ✨ Examples @@ -499,6 +499,10 @@ In general, **any contribution or feedback is welcome** (and encouraged!). ## 📃 Versioning +- v2.0.0-dev.1 - 23 March 2024 + +*** + - v1.1.0 - 13 August 2023 - **v1.0.0** - 26 July 2023 diff --git a/packages/fpdart/example/main.dart b/packages/fpdart/example/main.dart index ab73b3a..8391106 100644 --- a/packages/fpdart/example/main.dart +++ b/packages/fpdart/example/main.dart @@ -1 +1,14 @@ -void main() {} +import 'package:fpdart/fpdart.dart'; + +typedef Env = ({String url, int seed}); +typedef Error = String; +typedef Success = int; + +final either = Right<Error, Success>(10); +final option = Some(10); + +final effect = Effect<Env, Error, Success>.gen(($) { + final eitherValue = $.sync(either); + final optionValue = $.sync(option); + return eitherValue + optionValue; +}); diff --git a/packages/fpdart/example/screenshot_fpdart.png b/packages/fpdart/example/screenshot_fpdart.png index 4d120e5b53ffec0d5fc82c9ca62aaa32c80a96a6..3ce6144700c7a19a192c9aa5dffe0c0494df5d68 100644 GIT binary patch literal 182066 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	)N5y$<h0kmt{DGVkZm$SR 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 V`LiZviSYbURn$_bk~4q*{{Y(500IC2 literal 153192 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%0AT&oj{6=jVijvn$~+MruNf~t2~J;QMO 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 diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 5bf4de3..aa63f20 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -282,11 +282,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// /// {@category do_notation} Effect<V, L, R> provide<V>(E Function(V env) f) => Effect._( + // ignore: null_check_on_nullable_type_parameter (env) => _unsafeRun(f(env!)), ); /// {@category do_notation} static Effect<E, L, E> env<E, L>() => Effect._( + // ignore: null_check_on_nullable_type_parameter (env) => Right(env!), ); diff --git a/packages/fpdart/lib/src/extension/curry_extension.dart b/packages/fpdart/lib/src/extension/curry_extension.dart index 2b1ca4e..8bb1a96 100644 --- a/packages/fpdart/lib/src/extension/curry_extension.dart +++ b/packages/fpdart/lib/src/extension/curry_extension.dart @@ -1,10 +1,10 @@ -/// {@template fpdart_curry_extension} -/// Extract first parameter from this function to allow curring. -/// {@endtemplate} -/// -/// {@template fpdart_curry_last_extension} -/// Extract **last** parameter from this function to allow curring. -/// {@endtemplate} +// {@template fpdart_curry_extension} +// Extract first parameter from this function to allow curring. +// {@endtemplate} + +// {@template fpdart_curry_last_extension} +// Extract **last** parameter from this function to allow curring. +// {@endtemplate} extension CurryExtension2<Input1, Input2, Output> on Output Function( Input1, Input2) { diff --git a/packages/fpdart/pubspec.yaml b/packages/fpdart/pubspec.yaml index b952df4..fe0b5fe 100644 --- a/packages/fpdart/pubspec.yaml +++ b/packages/fpdart/pubspec.yaml @@ -16,9 +16,9 @@ dependencies: meta: ^1.12.0 dev_dependencies: - lints: ^2.0.1 - test: ^1.23.1 - collection: ^1.17.2 + lints: ^3.0.0 + test: ^1.25.2 + collection: ^1.18.0 screenshots: - description: "Basic usage of fpdart Option, Either, TaskEither types." From de6395da584e4c4f4c6e0cadb7c7482e57fb4357 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 24 Mar 2024 06:57:05 +0900 Subject: [PATCH 52/91] no `Never` env, maybe `void` --- packages/fpdart/lib/src/effect.dart | 42 +++--------- .../src/extension/future_or_extension.dart | 7 +- .../src/effect/effect_constructors_test.dart | 64 +++++++++---------- 3 files changed, 46 insertions(+), 67 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index aa63f20..9d05367 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -23,7 +23,7 @@ final class _EffectThrow<L> implements Exception { } } -EffectGen<E, L> _effectGen<E, L>(E? env) => ( +EffectGen<E, L> _effectGen<E, L>(E env) => ( async: <A>(effect) => Future.sync( () => effect.asEffect._unsafeRun(env).then( (exit) => switch (exit) { @@ -59,11 +59,7 @@ abstract interface class IEffect<E, L, R> { } final class Effect<E, L, R> extends IEffect<E, L, R> { - /// `E?` is optional to allow [Never] to work. - /// - /// In practice a user of the library should never be allowed to pass `null` as [E]. - final FutureOr<Exit<L, R>> Function(E? env) _unsafeRun; - + final FutureOr<Exit<L, R>> Function(E env) _unsafeRun; const Effect._(this._unsafeRun); @override @@ -155,10 +151,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { try { return f(_effectGen<E, L>(env)).then( Right.new, - onError: (dynamic error) { + onError: (error) { if (error is _EffectThrow<L>) { return Left<Cause<L>, R>(error.cause); } + + return Left<Cause<L>, R>(Die.current(error)); }, ); } on _EffectThrow<L> catch (genError) { @@ -282,14 +280,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// /// {@category do_notation} Effect<V, L, R> provide<V>(E Function(V env) f) => Effect._( - // ignore: null_check_on_nullable_type_parameter - (env) => _unsafeRun(f(env!)), + (env) => _unsafeRun(f(env)), ); /// {@category do_notation} static Effect<E, L, E> env<E, L>() => Effect._( - // ignore: null_check_on_nullable_type_parameter - (env) => Right(env!), + (env) => Right(env), ); /// {@category combining} @@ -555,31 +551,11 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); } -extension ProvideNever<L, R> on Effect<Never, L, R> { - /// Add a required dependency instead of [Never]. +extension ProvideVoid<L, R> on Effect<void, L, R> { + /// Add a required dependency instead of [void]. /// /// {@category do_notation} Effect<V, L, R> withEnv<V>() => Effect._( (env) => _unsafeRun(null), ); - - /// {@category execution} - R runSyncNoEnv() => Effect<void, L, R>._( - (_) => _unsafeRun(null), - ).runSync(null); - - /// {@category execution} - Exit<L, R> runSyncExitNoEnv() => Effect<void, L, R>._( - (_) => _unsafeRun(null), - ).runSyncExit(null); - - /// {@category execution} - Future<R> runFutureNoEnv() async => Effect<void, L, R>._( - (_) => _unsafeRun(null), - ).runFuture(null); - - /// {@category execution} - Future<Exit<L, R>> runFutureExitNoEnv() async => Effect<void, L, R>._( - (_) => _unsafeRun(null), - ).runFutureExit(null); } diff --git a/packages/fpdart/lib/src/extension/future_or_extension.dart b/packages/fpdart/lib/src/extension/future_or_extension.dart index d3fac8f..fb28b14 100644 --- a/packages/fpdart/lib/src/extension/future_or_extension.dart +++ b/packages/fpdart/lib/src/extension/future_or_extension.dart @@ -1,9 +1,12 @@ import 'dart:async'; extension FutureOrThenExtension<A> on FutureOr<A> { - FutureOr<B> then<B>(FutureOr<B> Function(A a) f, {Function? onError}) => + FutureOr<B> then<B>(FutureOr<B> Function(A a) f, + {B Function(Object error)? onError}) => switch (this) { - final Future<A> self => self.then(f, onError: onError), + final Future<A> self => self.then(f, onError: (Object error) { + if (onError != null) onError(error); + }), final A self => f(self), }; } diff --git a/packages/fpdart/test/src/effect/effect_constructors_test.dart b/packages/fpdart/test/src/effect/effect_constructors_test.dart index 5f54418..8197216 100644 --- a/packages/fpdart/test/src/effect/effect_constructors_test.dart +++ b/packages/fpdart/test/src/effect/effect_constructors_test.dart @@ -49,46 +49,46 @@ void main() { final result = main.runSync(null); expect(result, 10); }); + }); - group('gen', () { - test('sync succeed', () { - final main = Effect.gen(($) { - final value = $.sync(Effect.succeed(10)); - return value; - }); - final result = main.runSyncNoEnv(); - expect(result, 10); + group('gen', () { + test('sync succeed', () { + final main = Effect<void, Never, int>.gen(($) { + final value = $.sync(Effect.succeed(10)); + return value; }); + final result = main.runSync(null); + expect(result, 10); + }); - test('sync fail', () { - final main = Effect<Never, String, int>.gen(($) { - final value = $.sync(Effect.fail("abc")); - return value; - }); - final result = main.flip().runSyncNoEnv(); - expect(result, "abc"); + test('sync fail', () { + final main = Effect<void, String, int>.gen(($) { + final value = $.sync(Effect.fail("abc")); + return value; }); + final result = main.flip().runSync(null); + expect(result, "abc"); + }); - test('async succeed', () async { - final main = Effect.gen(($) async { - final value = - await $.async(Effect.functionSucceed(() => Future.value(10))); - return value; - }); - final result = await main.runFutureNoEnv(); - expect(result, 10); + test('async succeed', () async { + final main = Effect<void, Never, int>.gen(($) async { + final value = + await $.async(Effect.functionSucceed(() => Future.value(10))); + return value; }); + final result = await main.runFuture(null); + expect(result, 10); + }); - test('fail when running async as sync', () async { - final main = Effect.gen(($) { - final value = $.sync(Effect.functionSucceed( - () async => Future.value(10), - )); - return value; - }); - - expect(() => main.runSyncNoEnv(), throwsA(isA<Die>())); + test('fail when running async as sync', () async { + final main = Effect<void, Never, int>.gen(($) { + final value = $.sync(Effect.functionSucceed( + () async => Future.value(10), + )); + return value; }); + + expect(() => main.runSync(null), throwsA(isA<Die>())); }); }); }, From 60290a9470fcf5d63cffeef1e446ba226fca4098 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 05:21:06 +0900 Subject: [PATCH 53/91] `getSomes`, `getLefts`, `getRights` --- packages/fpdart/lib/src/either.dart | 16 ++++++++++++++++ .../lib/src/extension/iterable_extension.dart | 11 +++++++++-- packages/fpdart/lib/src/option.dart | 8 ++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index fc2a335..efd24bf 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -38,6 +38,22 @@ sealed class Either<L, R> extends IEffect<Never, L, R> { ) => value is R ? Right(value) : Left(onError(value)); + static Iterable<R> getRights<L, R>(Iterable<Either<L, R>> iterable) sync* { + for (var either in iterable) { + if (either is Right<L, R>) { + yield either.value; + } + } + } + + static Iterable<L> getLefts<L, R>(Iterable<Either<L, R>> iterable) sync* { + for (var either in iterable) { + if (either is Left<L, R>) { + yield either.value; + } + } + } + R? toNullable(); Option<R> toOption(); Either<L, C> flatMap<C>(Either<L, C> Function(R r) f); diff --git a/packages/fpdart/lib/src/extension/iterable_extension.dart b/packages/fpdart/lib/src/extension/iterable_extension.dart index 8d8a13c..64cc42e 100644 --- a/packages/fpdart/lib/src/extension/iterable_extension.dart +++ b/packages/fpdart/lib/src/extension/iterable_extension.dart @@ -11,7 +11,6 @@ import '../order.dart'; /// If the [Iterable] is empty, return [None]. /// {@endtemplate} -/// Functional programming functions on a mutable dart [Iterable] using `fpdart`. extension FpdartOnIterable<T> on Iterable<T> { /// {@macro fpdart_iterable_extension_head} /// @@ -407,8 +406,16 @@ extension FpdartOnIterable<T> on Iterable<T> { ); } -/// Functional programming functions on `Iterable<Iterable<T>>` using `fpdart`. extension FpdartOnIterableOfIterable<T> on Iterable<Iterable<T>> { /// From a `Iterable<Iterable<T>>` return a `Iterable<T>` of their concatenation. Iterable<T> get flatten => expand(identity); } + +extension FpdartSequenceIterableOption<R> on Iterable<Option<R>> { + Iterable<R> get getSomes => Option.getSomes(this); +} + +extension FpdartSequenceIterableEither<L, R> on Iterable<Either<L, R>> { + Iterable<R> get getRights => Either.getRights(this); + Iterable<L> get getLefts => Either.getLefts(this); +} diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index 27e4a2f..8fd0a2d 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -29,6 +29,14 @@ sealed class Option<R> extends IEffect<Never, Never, R> { } } + static Iterable<R> getSomes<R>(Iterable<Option<R>> iterable) sync* { + for (var option in iterable) { + if (option is Some<R>) { + yield option.value; + } + } + } + R? toNullable(); Option<C> flatMap<C>(Option<C> Function(R r) f); From d3307685dc531794f6bbd71f26fd1c9b7f71d83c Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 05:37:31 +0900 Subject: [PATCH 54/91] filtering --- packages/fpdart/lib/src/effect.dart | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 9d05367..c311760 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -549,6 +549,37 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ._unsafeRun(env), ), ); + + /// {@category filtering} + Effect<E, L, R> filterOrDie<C>({ + required bool Function(R r) predicate, + required C Function(R r) orDieWith, + }) => + Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => Left(cause), + Right(value: final value) => predicate(value) + ? Right(value) + : Left(Die.current(orDieWith(value))), + }, + ), + ); + + /// {@category filtering} + Effect<E, L, R> filterOrElse({ + required bool Function(R r) predicate, + required Effect<E, L, R> Function(R r) orElse, + }) => + Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => Left(cause), + Right(value: final value) => + predicate(value) ? Right(value) : orElse(value)._unsafeRun(env), + }, + ), + ); } extension ProvideVoid<L, R> on Effect<void, L, R> { From 8ad0f09ba3049a55ff8521b70320f0b989e3f1c5 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 05:59:48 +0900 Subject: [PATCH 55/91] full `Option` API --- packages/fpdart/lib/src/option.dart | 51 ++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index 8fd0a2d..77cd0f8 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -29,6 +29,12 @@ sealed class Option<R> extends IEffect<Never, Never, R> { } } + factory Option.fromJson( + dynamic json, + R Function(dynamic json) fromJson, + ) => + json != null ? Option.tryCatch(() => fromJson(json)) : None(); + static Iterable<R> getSomes<R>(Iterable<Option<R>> iterable) sync* { for (var option in iterable) { if (option is Some<R>) { @@ -37,8 +43,13 @@ sealed class Option<R> extends IEffect<Never, Never, R> { } } + Object? toJson(Object? Function(R value) toJson); R? toNullable(); Option<C> flatMap<C>(Option<C> Function(R r) f); + Option<C> andThen<C>(C Function(R r) f); + Option<R> tap<C>(Option<C> Function(R r) f); + Option<R> filter(bool Function(R r) f); + Option<C> filterMap<C>(Option<C> Function(R r) f); Option<V> ap<V>( Option<V Function(R r)> f, @@ -74,17 +85,21 @@ final class Some<R> extends Option<R> { @override Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect.succeed(value); - Option<C> andThen<C>(Option<C> Function() then) => then(); - @override Option<C> flatMap<C>(Option<C> Function(R r) f) => f(value); @override R toNullable() => value; + @override + Option<C> andThen<C>(C Function(R r) f) => Some(f(value)); + @override Either<L, R> toEither<L>(L Function() onLeft) => Right(value); + @override + Object? toJson(Object? Function(R value) toJson) => toJson(value); + @override bool operator ==(Object other) => (other is Some) && other.value == value; @@ -93,6 +108,21 @@ final class Some<R> extends Option<R> { @override String toString() => 'Some($value)'; + + @override + Option<R> tap<C>(Option<C> Function(R r) f) => f(value).map((_) => value); + + @override + Option<R> filter(bool Function(R r) f) { + if (f(value)) return Some(value); + return None(); + } + + @override + Option<C> filterMap<C>(Option<C> Function(R r) f) { + if (f(value) case Some(value: final value)) return Some(value); + return None(); + } } final class None extends Option<Never> { @@ -106,14 +136,27 @@ final class None extends Option<Never> { // ignore: cast_from_null_always_fails Effect._((_) => Left(Fail(null as Never))); - Option<C> andThen<C>(Option<C> Function() then) => this; - @override Option<C> flatMap<C>(Option<C> Function(Never r) f) => this; @override Null toNullable() => null; + @override + Object? toJson(Object? Function(Never value) toJson) => None(); + @override String toString() => 'None'; + + @override + Option<C> andThen<C>(C Function(Never r) f) => None(); + + @override + Option<Never> tap<C>(Option<C> Function(Never r) f) => None(); + + @override + Option<Never> filter(bool Function(Never r) f) => None(); + + @override + Option<C> filterMap<C>(Option<C> Function(Never r) f) => None(); } From b73849d4800db4fe40672c429d4558d9124bac46 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 06:13:13 +0900 Subject: [PATCH 56/91] full `Either` API --- packages/fpdart/lib/src/either.dart | 61 +++++++++++++++++++++++------ packages/fpdart/lib/src/option.dart | 6 +-- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index efd24bf..f1de423 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -54,8 +54,7 @@ sealed class Either<L, R> extends IEffect<Never, L, R> { } } - R? toNullable(); - Option<R> toOption(); + R? getOrNull(); Either<L, C> flatMap<C>(Either<L, C> Function(R r) f); Either<C, R> mapLeft<C>(C Function(L l) f); Effect<V, L, R> provide<V>(); @@ -63,8 +62,17 @@ sealed class Either<L, R> extends IEffect<Never, L, R> { required D Function(L l) onLeft, required C Function(R r) onRight, }); - Either<R, L> get flip; + Either<R, L> flip(); R getOrElse(R Function(L l) orElse); + Either<L, C> andThen<C>(C Function(R r) f); + Either<C, R> orElse<C>(Either<C, R> Function(L l) orElse); + Either<L, R> tap<C>(Either<L, C> Function(R r) f); + Either<L, R> filterOrLeft<C>({ + required bool Function(R r) predicate, + required L Function(R r) orLeftWith, + }); + Option<L> getLeft(); + Option<R> getRight(); Either<L, V> ap<V>( Either<L, V Function(R r)> f, @@ -85,15 +93,17 @@ final class Right<L, R> extends Either<L, R> { @override Effect<Never, L, R> get asEffect => Effect._((_) => Right(value)); - Either<L, C> andThen<C>(Either<L, C> Function() then) => then(); + @override + Either<L, C> andThen<C>(C Function(R value) f) => Right(f(value)); + @override Either<C, R> orElse<C>(Either<C, R> Function(L l) orElse) => Right(value); @override R getOrElse(R Function(L l) orElse) => value; @override - Either<R, L> get flip => Left(value); + Either<R, L> flip() => Left(value); @override Either<D, C> mapBoth<C, D>( @@ -111,10 +121,10 @@ final class Right<L, R> extends Either<L, R> { Effect<V, L, R> provide<V>() => Effect.succeed(value); @override - R toNullable() => value; + R getOrNull() => value; @override - Option<R> toOption() => Some(value); + Option<R> getRight() => Some(value); @override bool operator ==(Object other) => (other is Right) && other.value == value; @@ -124,6 +134,20 @@ final class Right<L, R> extends Either<L, R> { @override String toString() => 'Right($value)'; + + @override + Either<L, R> filterOrLeft<C>({ + required bool Function(R r) predicate, + required L Function(R r) orLeftWith, + }) => + predicate(value) ? Right(value) : Left(orLeftWith(value)); + + @override + Option<L> getLeft() => None(); + + @override + Either<L, R> tap<C>(Either<L, C> Function(R r) f) => + f(value).map((_) => value); } final class Left<L, R> extends Either<L, R> { @@ -133,15 +157,17 @@ final class Left<L, R> extends Either<L, R> { @override Effect<Never, L, R> get asEffect => Effect._((_) => Left(Fail(value))); - Either<L, C> andThen<C>(Either<L, C> Function() then) => Left(value); + @override + Either<L, C> andThen<C>(C Function(R value) f) => Left(value); + @override Either<C, R> orElse<C>(Either<C, R> Function(L l) orElse) => orElse(value); @override R getOrElse(R Function(L l) orElse) => orElse(value); @override - Either<R, L> get flip => Right(value); + Either<R, L> flip() => Right(value); @override Either<D, C> mapBoth<C, D>( @@ -159,10 +185,10 @@ final class Left<L, R> extends Either<L, R> { Effect<V, L, R> provide<V>() => Effect.fail(value); @override - R? toNullable() => null; + R? getOrNull() => null; @override - Option<R> toOption() => None(); + Option<R> getRight() => None(); @override bool operator ==(Object other) => (other is Left) && other.value == value; @@ -172,4 +198,17 @@ final class Left<L, R> extends Either<L, R> { @override String toString() => 'Left($value)'; + + @override + Either<L, R> filterOrLeft<C>({ + required bool Function(R r) predicate, + required L Function(R r) orLeftWith, + }) => + Left(value); + + @override + Option<L> getLeft() => Some(value); + + @override + Either<L, R> tap<C>(Either<L, C> Function(R r) f) => Left(value); } diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index 77cd0f8..7d22876 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -44,7 +44,7 @@ sealed class Option<R> extends IEffect<Never, Never, R> { } Object? toJson(Object? Function(R value) toJson); - R? toNullable(); + R? getOrNull(); Option<C> flatMap<C>(Option<C> Function(R r) f); Option<C> andThen<C>(C Function(R r) f); Option<R> tap<C>(Option<C> Function(R r) f); @@ -89,7 +89,7 @@ final class Some<R> extends Option<R> { Option<C> flatMap<C>(Option<C> Function(R r) f) => f(value); @override - R toNullable() => value; + R getOrNull() => value; @override Option<C> andThen<C>(C Function(R r) f) => Some(f(value)); @@ -140,7 +140,7 @@ final class None extends Option<Never> { Option<C> flatMap<C>(Option<C> Function(Never r) f) => this; @override - Null toNullable() => null; + Null getOrNull() => null; @override Object? toJson(Object? Function(Never value) toJson) => None(); From 229e536e219dfd3f538d9910c6373d7bbd0c67a8 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 06:15:25 +0900 Subject: [PATCH 57/91] rename `Fail` to `Failure` --- packages/fpdart/lib/src/effect.dart | 35 +++++++++++++++-------------- packages/fpdart/lib/src/either.dart | 2 +- packages/fpdart/lib/src/exit.dart | 8 +++---- packages/fpdart/lib/src/option.dart | 4 ++-- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index c311760..88d6813 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -176,7 +176,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { try { return execute().then(Right.new); } catch (err, stack) { - return Left(Fail(onError(err, stack), stack)); + return Left(Failure(onError(err, stack), stack)); } }, ); @@ -184,7 +184,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category constructors} factory Effect.fromNullable(R? value, {required L Function() onNull}) => Effect._( - (_) => value == null ? Left(Fail(onNull())) : Right(value), + (_) => value == null ? Left(Failure(onNull())) : Right(value), ); /// {@category constructors} @@ -198,7 +198,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category constructors} - factory Effect.fail(L value) => Effect._((_) => Left(Fail(value))); + factory Effect.fail(L value) => Effect._((_) => Left(Failure(value))); /// {@category constructors} factory Effect.succeed(R value) => Effect._((_) => Right(value)); @@ -303,7 +303,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail<L>(error: final error) => Right(Left(error)), + Failure<L>(error: final error) => Right(Left(error)), Die() => Left(cause), }, Right(value: final value) => Right(Right(value)), @@ -316,7 +316,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail<L>() => Right(None()), + Failure<L>() => Right(None()), Die() => Left(cause), }, Right(value: final value) => Right(Some(value)), @@ -340,7 +340,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail<L>(error: final error) => Right(onFailure(error)), + Failure<L>(error: final error) => Right(onFailure(error)), Die() => Left(cause), }, Right(value: final value) => Right(onSuccess(value)), @@ -371,7 +371,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail<L>(error: final error) => onFailure(error)._unsafeRun(env), + Failure<L>(error: final error) => + onFailure(error)._unsafeRun(env), Die() => Left(cause), }, Right(value: final value) => onSuccess(value)._unsafeRun(env), @@ -398,10 +399,10 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail<L>(error: final error) => Right(error), + Failure<L>(error: final error) => Right(error), Die() => Left(cause), }, - Right(value: final value) => Left(Fail(value)), + Right(value: final value) => Left(Failure(value)), }, ), ); @@ -414,7 +415,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail<L>(error: final error) => Left(Fail(f(error))), + Failure<L>(error: final error) => Left(Failure(f(error))), Die() => Left(cause), }, Right(value: final value) => Right(value), @@ -426,7 +427,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, C, R> mapErrorCause<C>(C Function(Cause<L> l) f) => Effect._( (env) => _unsafeRun(env).then( (exit) => switch (exit) { - Left(value: final cause) => Left(Fail(f(cause))), + Left(value: final cause) => Left(Failure(f(cause))), Right(value: final value) => Right(value), }, ), @@ -438,7 +439,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail<L>(error: final error) => Left(Fail(fl(error))), + Failure<L>(error: final error) => Left(Failure(fl(error))), Die() => Left(cause), }, Right(value: final value) => Right(fr(value)), @@ -465,8 +466,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail<L>(error: final error) => f(error)._unsafeRun(env).then( - (_) => Left(Fail(error)), + Failure<L>(error: final error) => f(error)._unsafeRun(env).then( + (_) => Left(Failure(error)), ), Die() => Left(cause), }, @@ -483,7 +484,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail<L>(error: final error) => orElse(error)._unsafeRun(env), + Failure<L>(error: final error) => orElse(error)._unsafeRun(env), Die() => Left(cause), }, Right(value: final value) => @@ -509,7 +510,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail<L>(error: final error) => + Failure<L>(error: final error) => Left(Die.current(onError(error))), Die() => Left(cause), }, @@ -527,7 +528,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail<L>(error: final error) => f(error)._unsafeRun(env), + Failure<L>(error: final error) => f(error)._unsafeRun(env), Die() => Left(cause), }, Right(value: final value) => diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index f1de423..c59098e 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -155,7 +155,7 @@ final class Left<L, R> extends Either<L, R> { const Left(this.value); @override - Effect<Never, L, R> get asEffect => Effect._((_) => Left(Fail(value))); + Effect<Never, L, R> get asEffect => Effect._((_) => Left(Failure(value))); @override Either<L, C> andThen<C>(C Function(R value) f) => Left(value); diff --git a/packages/fpdart/lib/src/exit.dart b/packages/fpdart/lib/src/exit.dart index de225cf..b646142 100644 --- a/packages/fpdart/lib/src/exit.dart +++ b/packages/fpdart/lib/src/exit.dart @@ -20,7 +20,7 @@ final class Die extends Cause<Never> { Die(error, StackTrace.current, stackTrace); @override - bool operator ==(Object other) => (other is Fail) && other.error == error; + bool operator ==(Object other) => (other is Failure) && other.error == error; @override int get hashCode => error.hashCode; @@ -32,16 +32,16 @@ final class Die extends Cause<Never> { } /// Failed with an expected error -final class Fail<L> extends Cause<L> { +final class Failure<L> extends Cause<L> { final L error; @override final StackTrace? stackTrace; - const Fail(this.error, [this.stackTrace]); + const Failure(this.error, [this.stackTrace]); @override - bool operator ==(Object other) => (other is Fail) && other.error == error; + bool operator ==(Object other) => (other is Failure) && other.error == error; @override int get hashCode => error.hashCode; diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index 7d22876..5540c2f 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -69,7 +69,7 @@ sealed class Option<R> extends IEffect<Never, Never, R> { Effect<V, L, R> provide<L, V>(L Function() onNone) => Effect._( (env) => switch (this) { - None() => Left(Fail(onNone())), + None() => Left(Failure(onNone())), Some(value: final value) => Right(value), }, ); @@ -134,7 +134,7 @@ final class None extends Option<Never> { @override Effect<Never, Never, Never> get asEffect => // ignore: cast_from_null_always_fails - Effect._((_) => Left(Fail(null as Never))); + Effect._((_) => Left(Failure(null as Never))); @override Option<C> flatMap<C>(Option<C> Function(Never r) f) => this; From 8a0f930c8aa8f007ebed30b56db797ac5064ca27 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 06:21:54 +0900 Subject: [PATCH 58/91] errors in `FutureOr` sync --- .../src/extension/future_or_extension.dart | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/fpdart/lib/src/extension/future_or_extension.dart b/packages/fpdart/lib/src/extension/future_or_extension.dart index fb28b14..ab0117e 100644 --- a/packages/fpdart/lib/src/extension/future_or_extension.dart +++ b/packages/fpdart/lib/src/extension/future_or_extension.dart @@ -2,11 +2,22 @@ import 'dart:async'; extension FutureOrThenExtension<A> on FutureOr<A> { FutureOr<B> then<B>(FutureOr<B> Function(A a) f, - {B Function(Object error)? onError}) => - switch (this) { - final Future<A> self => self.then(f, onError: (Object error) { + {B Function(Object error)? onError}) { + switch (this) { + case Future<A> self: + return self.then( + f, + onError: (Object error) { if (onError != null) onError(error); - }), - final A self => f(self), - }; + }, + ); + case A self: + try { + return f(self); + } catch (error) { + if (onError != null) return onError(error); + rethrow; + } + } + } } From f02afe3d4eb4bde8ca45ee769def2b801c0abfa1 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 06:26:25 +0900 Subject: [PATCH 59/91] `onError` with stack trace --- packages/fpdart/lib/src/effect.dart | 14 ++++++++++---- .../lib/src/extension/future_or_extension.dart | 10 +++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 88d6813..1ddca83 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -1,8 +1,9 @@ import 'dart:async'; +import 'package:fpdart/fpdart.dart'; + import './extension/future_or_extension.dart'; import './extension/iterable_extension.dart'; -import 'exit.dart'; import 'unit.dart' as fpdart_unit; part 'either.dart'; @@ -151,12 +152,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { try { return f(_effectGen<E, L>(env)).then( Right.new, - onError: (error) { + onError: (error, stackTrace) { if (error is _EffectThrow<L>) { return Left<Cause<L>, R>(error.cause); } - return Left<Cause<L>, R>(Die.current(error)); + return Left<Cause<L>, R>(Die(error, stackTrace)); }, ); } on _EffectThrow<L> catch (genError) { @@ -174,7 +175,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect._( (env) { try { - return execute().then(Right.new); + return execute().then( + Right.new, + onError: (error, stackTrace) => Left( + Failure(onError(error, stackTrace), stackTrace), + ), + ); } catch (err, stack) { return Left(Failure(onError(err, stack), stack)); } diff --git a/packages/fpdart/lib/src/extension/future_or_extension.dart b/packages/fpdart/lib/src/extension/future_or_extension.dart index ab0117e..ef6d307 100644 --- a/packages/fpdart/lib/src/extension/future_or_extension.dart +++ b/packages/fpdart/lib/src/extension/future_or_extension.dart @@ -2,20 +2,20 @@ import 'dart:async'; extension FutureOrThenExtension<A> on FutureOr<A> { FutureOr<B> then<B>(FutureOr<B> Function(A a) f, - {B Function(Object error)? onError}) { + {B Function(Object error, StackTrace stackTrace)? onError}) { switch (this) { case Future<A> self: return self.then( f, - onError: (Object error) { - if (onError != null) onError(error); + onError: (Object error, StackTrace stackTrace) { + if (onError != null) onError(error, stackTrace); }, ); case A self: try { return f(self); - } catch (error) { - if (onError != null) return onError(error); + } catch (error, stackTrace) { + if (onError != null) return onError(error, stackTrace); rethrow; } } From df5740dd7952baf84bb761571a4ff9b863d1e7de Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 07:19:05 +0900 Subject: [PATCH 60/91] `provide` and `mapEnv` --- packages/fpdart/lib/src/effect.dart | 26 ++++++++++---- .../src/effect/effect_constructors_test.dart | 8 ++--- .../src/effect/effect_do_notation_test.dart | 36 +++++++++++++++++++ 3 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 packages/fpdart/test/src/effect/effect_do_notation_test.dart diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 1ddca83..0ff6293 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -203,6 +203,9 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (_) => f().then(Right.new), ); + /// {@category constructors} + factory Effect.from(Exit<L, R> Function(E env) f) => Effect._(f); + /// {@category constructors} factory Effect.fail(L value) => Effect._((_) => Left(Failure(value))); @@ -282,13 +285,14 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ) => flatMap((_) => effect); - /// Extract the required dependency from the complete environment. - /// /// {@category do_notation} - Effect<V, L, R> provide<V>(E Function(V env) f) => Effect._( + Effect<V, L, R> mapEnv<V>(E Function(V env) f) => Effect._( (env) => _unsafeRun(f(env)), ); + /// {@category do_notation} + Effect<Null, L, R> provide(E env) => Effect._((_) => _unsafeRun(env)); + /// {@category do_notation} static Effect<E, L, E> env<E, L>() => Effect._( (env) => Right(env), @@ -589,11 +593,21 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); } -extension ProvideVoid<L, R> on Effect<void, L, R> { - /// Add a required dependency instead of [void]. - /// +extension ProvideNull<L, R> on Effect<Null, L, R> { /// {@category do_notation} Effect<V, L, R> withEnv<V>() => Effect._( (env) => _unsafeRun(null), ); + + /// {@category execution} + R runSyncVoid() => runSync(null); + + /// {@category execution} + Future<R> runFutureVoid() => runFuture(null); + + /// {@category execution} + Either<Cause<L>, R> runSyncExitVoid() => runSyncExit(null); + + /// {@category execution} + Future<Either<Cause<L>, R>> runFutureExitVoid() => runFutureExit(null); } diff --git a/packages/fpdart/test/src/effect/effect_constructors_test.dart b/packages/fpdart/test/src/effect/effect_constructors_test.dart index 8197216..e61d6ba 100644 --- a/packages/fpdart/test/src/effect/effect_constructors_test.dart +++ b/packages/fpdart/test/src/effect/effect_constructors_test.dart @@ -53,7 +53,7 @@ void main() { group('gen', () { test('sync succeed', () { - final main = Effect<void, Never, int>.gen(($) { + final main = Effect<Null, Never, int>.gen(($) { final value = $.sync(Effect.succeed(10)); return value; }); @@ -62,7 +62,7 @@ void main() { }); test('sync fail', () { - final main = Effect<void, String, int>.gen(($) { + final main = Effect<Null, String, int>.gen(($) { final value = $.sync(Effect.fail("abc")); return value; }); @@ -71,7 +71,7 @@ void main() { }); test('async succeed', () async { - final main = Effect<void, Never, int>.gen(($) async { + final main = Effect<Null, Never, int>.gen(($) async { final value = await $.async(Effect.functionSucceed(() => Future.value(10))); return value; @@ -81,7 +81,7 @@ void main() { }); test('fail when running async as sync', () async { - final main = Effect<void, Never, int>.gen(($) { + final main = Effect<Null, Never, int>.gen(($) { final value = $.sync(Effect.functionSucceed( () async => Future.value(10), )); diff --git a/packages/fpdart/test/src/effect/effect_do_notation_test.dart b/packages/fpdart/test/src/effect/effect_do_notation_test.dart new file mode 100644 index 0000000..aa476ee --- /dev/null +++ b/packages/fpdart/test/src/effect/effect_do_notation_test.dart @@ -0,0 +1,36 @@ +import "package:fpdart/fpdart.dart"; +import "package:test/test.dart"; + +void main() { + group( + "Effect do notation", + () { + group('provide', () { + test('remove dependency', () { + final main = Effect<String, String, int>.gen(($) { + final env = $.sync(Effect.env()); + return env.length; + }); + + final program = main.provide("abc"); + final result = program.runSyncVoid(); + expect(result, 3); + }); + }); + + group('mapEnv', () { + test('adapt dependency from another program', () { + final subMain = + Effect<int, String, int>.from((env) => Right(env + 1)); + final main = Effect<String, String, int>.gen(($) { + final value = $.sync(subMain.mapEnv((env) => env.length)); + return value; + }); + + final result = main.runSync("abc"); + expect(result, 4); + }); + }); + }, + ); +} From a3352b5de8e1c9512ce7abb66834c500fc072f10 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 07:25:18 +0900 Subject: [PATCH 61/91] `provideEffect` --- packages/fpdart/lib/src/effect.dart | 10 ++++++++ .../src/effect/effect_do_notation_test.dart | 24 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 0ff6293..eae791d 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -293,6 +293,16 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category do_notation} Effect<Null, L, R> provide(E env) => Effect._((_) => _unsafeRun(env)); + /// {@category do_notation} + Effect<V, L, R> provideEffect<V>(Effect<V, L, E> effect) => Effect._( + (env) => effect._unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => Left(cause), + Right(value: final value) => _unsafeRun(value), + }, + ), + ); + /// {@category do_notation} static Effect<E, L, E> env<E, L>() => Effect._( (env) => Right(env), diff --git a/packages/fpdart/test/src/effect/effect_do_notation_test.dart b/packages/fpdart/test/src/effect/effect_do_notation_test.dart index aa476ee..a6e4dfb 100644 --- a/packages/fpdart/test/src/effect/effect_do_notation_test.dart +++ b/packages/fpdart/test/src/effect/effect_do_notation_test.dart @@ -18,6 +18,30 @@ void main() { }); }); + group('provideEffect', () { + test('valid dependency', () { + final main = Effect<int, String, int>.gen(($) { + final env = $.sync(Effect.env()); + return env + 1; + }); + + final program = main.provideEffect<Null>(Effect.succeed(10)); + final result = program.runSyncVoid(); + expect(result, 11); + }); + + test('invalid dependency', () { + final main = Effect<int, String, int>.gen(($) { + final env = $.sync(Effect.env()); + return env + 1; + }); + + final program = main.provideEffect<Null>(Effect.fail("error")); + final result = program.flip().runSyncVoid(); + expect(result, "error"); + }); + }); + group('mapEnv', () { test('adapt dependency from another program', () { final subMain = From d8144dd042fbc085301da48b66f6b78fc32a1e44 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 07:55:30 +0900 Subject: [PATCH 62/91] test type parameters --- .../fpdart/test/src/effect/effect_do_notation_test.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/fpdart/test/src/effect/effect_do_notation_test.dart b/packages/fpdart/test/src/effect/effect_do_notation_test.dart index a6e4dfb..bbb4daf 100644 --- a/packages/fpdart/test/src/effect/effect_do_notation_test.dart +++ b/packages/fpdart/test/src/effect/effect_do_notation_test.dart @@ -25,7 +25,8 @@ void main() { return env + 1; }); - final program = main.provideEffect<Null>(Effect.succeed(10)); + final program = + main.provideEffect(Effect<Null, String, int>.succeed(10)); final result = program.runSyncVoid(); expect(result, 11); }); @@ -36,7 +37,8 @@ void main() { return env + 1; }); - final program = main.provideEffect<Null>(Effect.fail("error")); + final program = + main.provideEffect(Effect<Null, String, int>.fail("error")); final result = program.flip().runSyncVoid(); expect(result, "error"); }); From dcb98619043e5271e1c459fe63d2e009b10b8dae Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 16:14:20 +0900 Subject: [PATCH 63/91] run only when env is `Null` --- packages/fpdart/lib/src/effect.dart | 146 ++++++++---------- .../src/effect/effect_alternatives_test.dart | 18 ++- .../src/effect/effect_collecting_test.dart | 8 +- .../src/effect/effect_constructors_test.dart | 30 ++-- .../src/effect/effect_do_notation_test.dart | 8 +- .../src/effect/effect_sequencing_test.dart | 14 +- 6 files changed, 108 insertions(+), 116 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index eae791d..8d968ca 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -71,81 +71,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { return "Effect(${_unsafeRun.runtimeType})"; } - /// {@category execution} - R runSync(E env) { - try { - final result = _unsafeRun(env); - if (result is Future) { - throw Die.current( - Exception("runSync cannot execute async Effect"), - ); - } - - return switch (result) { - Left(value: final cause) => throw cause, - Right(value: final value) => value, - }; - } on Cause<L> { - rethrow; - } catch (error, stackTrace) { - throw Die(error, stackTrace); - } - } - - /// {@category execution} - Exit<L, R> runSyncExit(E env) { - try { - final result = _unsafeRun(env); - if (result is Future) { - return Left(Die.current( - Exception("runSyncExit cannot execute async Effect"), - )); - } - return result; - } on Cause<L> catch (cause) { - return Left(cause); - } catch (error, stackTrace) { - return Left(Die(error, stackTrace)); - } - } - - /// {@category execution} - Future<R> runFuture(E env) async { - try { - final result = _unsafeRun(env); - if (result is! Future) { - return switch (result) { - Left(value: final cause) => throw cause, - Right(value: final value) => value, - }; - } - - return switch (await result) { - Left(value: final cause) => throw cause, - Right(value: final value) => value, - }; - } on Cause<L> { - rethrow; - } catch (error, stackTrace) { - throw Die(error, stackTrace); - } - } - - /// {@category execution} - Future<Exit<L, R>> runFutureExit(E env) async { - try { - final result = _unsafeRun(env); - if (result is! Future) { - return result; - } - return result; - } on Cause<L> catch (cause) { - return Left(cause); - } catch (error, stackTrace) { - return Left(Die(error, stackTrace)); - } - } - /// {@category constructors} factory Effect.gen(DoFunctionEffect<E, L, R> f) => Effect<E, L, R>._( (env) { @@ -610,14 +535,77 @@ extension ProvideNull<L, R> on Effect<Null, L, R> { ); /// {@category execution} - R runSyncVoid() => runSync(null); + R runSync() { + try { + final result = _unsafeRun(null); + if (result is Future) { + throw Die.current( + Exception("runSync cannot execute async Effect"), + ); + } + + return switch (result) { + Left(value: final cause) => throw cause, + Right(value: final value) => value, + }; + } on Cause<L> { + rethrow; + } catch (error, stackTrace) { + throw Die(error, stackTrace); + } + } /// {@category execution} - Future<R> runFutureVoid() => runFuture(null); + Exit<L, R> runSyncExit() { + try { + final result = _unsafeRun(null); + if (result is Future) { + return Left(Die.current( + Exception("runSyncExit cannot execute async Effect"), + )); + } + return result; + } on Cause<L> catch (cause) { + return Left(cause); + } catch (error, stackTrace) { + return Left(Die(error, stackTrace)); + } + } /// {@category execution} - Either<Cause<L>, R> runSyncExitVoid() => runSyncExit(null); + Future<R> runFuture() async { + try { + final result = _unsafeRun(null); + if (result is! Future) { + return switch (result) { + Left(value: final cause) => throw cause, + Right(value: final value) => value, + }; + } + + return switch (await result) { + Left(value: final cause) => throw cause, + Right(value: final value) => value, + }; + } on Cause<L> { + rethrow; + } catch (error, stackTrace) { + throw Die(error, stackTrace); + } + } /// {@category execution} - Future<Either<Cause<L>, R>> runFutureExitVoid() => runFutureExit(null); + Future<Exit<L, R>> runFutureExit() async { + try { + final result = _unsafeRun(null); + if (result is! Future) { + return result; + } + return result; + } on Cause<L> catch (cause) { + return Left(cause); + } catch (error, stackTrace) { + return Left(Die(error, stackTrace)); + } + } } diff --git a/packages/fpdart/test/src/effect/effect_alternatives_test.dart b/packages/fpdart/test/src/effect/effect_alternatives_test.dart index c4884a0..73dde6f 100644 --- a/packages/fpdart/test/src/effect/effect_alternatives_test.dart +++ b/packages/fpdart/test/src/effect/effect_alternatives_test.dart @@ -9,27 +9,29 @@ void main() { () { group('orDie', () { test('succeed', () { - final main = Effect.succeed(10).orDie; - final result = main.runSync(null); + final main = Effect<Null, String, int>.succeed(10).orDie; + final result = main.runSync(); expect(result, 10); }); test('fail', () { - final main = Effect.fail(10).orDie; - expect(() => main.runSync(null), throwsA(isA<Die>())); + final main = Effect<Null, String, int>.fail("error").orDie; + expect(() => main.runSync(), throwsA(isA<Die>())); }); }); group('orDieWith', () { test('succeed', () { - final main = Effect.succeed(10).orDieWith((_) => CustomError()); - final result = main.runSync(null); + final main = Effect<Null, String, int>.succeed(10) + .orDieWith((_) => CustomError()); + final result = main.runSync(); expect(result, 10); }); test('fail', () { - final main = Effect.fail(10).orDieWith((_) => CustomError()); - expect(() => main.runSync(null), throwsA(isA<Die>())); + final main = Effect<Null, String, int>.fail("error") + .orDieWith((_) => CustomError()); + expect(() => main.runSync(), throwsA(isA<Die>())); }); }); }, diff --git a/packages/fpdart/test/src/effect/effect_collecting_test.dart b/packages/fpdart/test/src/effect/effect_collecting_test.dart index a15c0f4..84db780 100644 --- a/packages/fpdart/test/src/effect/effect_collecting_test.dart +++ b/packages/fpdart/test/src/effect/effect_collecting_test.dart @@ -7,23 +7,23 @@ void main() { () { group('all', () { test('succeeded all', () { - final main = Effect.all([ + final main = Effect.all<Null, String, int>([ Effect.succeed(10), Effect.succeed(20), ]); - final result = main.runSync(null); + final result = main.runSync(); expect(result, [10, 20]); }); test('fail and stop execution', () { var mutable = 0; - final main = Effect.all<dynamic, String, int>([ + final main = Effect.all<Null, String, int>([ Effect.succeed(10), Effect.fail("10"), Effect.functionSucceed(() => mutable += 1), Effect.fail("0"), ]); - final result = main.flip().runSync(null); + final result = main.flip().runSync(); expect(mutable, 0); expect(result, "10"); }); diff --git a/packages/fpdart/test/src/effect/effect_constructors_test.dart b/packages/fpdart/test/src/effect/effect_constructors_test.dart index e61d6ba..0df5ab4 100644 --- a/packages/fpdart/test/src/effect/effect_constructors_test.dart +++ b/packages/fpdart/test/src/effect/effect_constructors_test.dart @@ -6,21 +6,21 @@ void main() { "Effect constructors", () { test('succeed', () { - final main = Effect.succeed(10); - final result = main.runSync(null); + final main = Effect<Null, String, int>.succeed(10); + final result = main.runSync(); expect(result, 10); }); test('fail', () { - final main = Effect.fail(10); - final result = main.flip().runSync(null); - expect(result, 10); + final main = Effect<Null, String, int>.fail("error"); + final result = main.flip().runSync(); + expect(result, "error"); }); group('tryCatch', () { test('executes once', () { var mutable = 0; - final main = Effect.tryCatch( + final main = Effect<Null, void, int>.tryCatch( execute: () { mutable += 1; return 10; @@ -28,25 +28,25 @@ void main() { onError: (error, stackTrace) {}, ); - main.runSync(null); + main.runSync(); expect(mutable, 1); }); test('async', () async { - final main = Effect.tryCatch( + final main = Effect<Null, void, int>.tryCatch( execute: () async => 10, onError: (error, stackTrace) {}, ); - final result = await main.runFuture(null); + final result = await main.runFuture(); expect(result, 10); }); test('sync', () { - final main = Effect.tryCatch( + final main = Effect<Null, void, int>.tryCatch( execute: () => 10, onError: (error, stackTrace) {}, ); - final result = main.runSync(null); + final result = main.runSync(); expect(result, 10); }); }); @@ -57,7 +57,7 @@ void main() { final value = $.sync(Effect.succeed(10)); return value; }); - final result = main.runSync(null); + final result = main.runSync(); expect(result, 10); }); @@ -66,7 +66,7 @@ void main() { final value = $.sync(Effect.fail("abc")); return value; }); - final result = main.flip().runSync(null); + final result = main.flip().runSync(); expect(result, "abc"); }); @@ -76,7 +76,7 @@ void main() { await $.async(Effect.functionSucceed(() => Future.value(10))); return value; }); - final result = await main.runFuture(null); + final result = await main.runFuture(); expect(result, 10); }); @@ -88,7 +88,7 @@ void main() { return value; }); - expect(() => main.runSync(null), throwsA(isA<Die>())); + expect(() => main.runSync(), throwsA(isA<Die>())); }); }); }, diff --git a/packages/fpdart/test/src/effect/effect_do_notation_test.dart b/packages/fpdart/test/src/effect/effect_do_notation_test.dart index bbb4daf..d3ff7d1 100644 --- a/packages/fpdart/test/src/effect/effect_do_notation_test.dart +++ b/packages/fpdart/test/src/effect/effect_do_notation_test.dart @@ -13,7 +13,7 @@ void main() { }); final program = main.provide("abc"); - final result = program.runSyncVoid(); + final result = program.runSync(); expect(result, 3); }); }); @@ -27,7 +27,7 @@ void main() { final program = main.provideEffect(Effect<Null, String, int>.succeed(10)); - final result = program.runSyncVoid(); + final result = program.runSync(); expect(result, 11); }); @@ -39,7 +39,7 @@ void main() { final program = main.provideEffect(Effect<Null, String, int>.fail("error")); - final result = program.flip().runSyncVoid(); + final result = program.flip().runSync(); expect(result, "error"); }); }); @@ -53,7 +53,7 @@ void main() { return value; }); - final result = main.runSync("abc"); + final result = main.provide("abc").runSync(); expect(result, 4); }); }); diff --git a/packages/fpdart/test/src/effect/effect_sequencing_test.dart b/packages/fpdart/test/src/effect/effect_sequencing_test.dart index 0480bd1..ee6c273 100644 --- a/packages/fpdart/test/src/effect/effect_sequencing_test.dart +++ b/packages/fpdart/test/src/effect/effect_sequencing_test.dart @@ -6,27 +6,29 @@ void main() { "Effect constructors", () { test('zipLeft', () { - final main = Effect.succeed(10).zipLeft(Effect.succeed("10")); - final result = main.runSync(null); + final main = + Effect<Null, String, int>.succeed(10).zipLeft(Effect.succeed("10")); + final result = main.runSync(); expect(result, 10); }); test('zipRight', () { - final main = Effect.succeed(10).zipRight(Effect.succeed("10")); - final result = main.runSync(null); + final main = Effect<Null, String, int>.succeed(10) + .zipRight(Effect.succeed("10")); + final result = main.runSync(); expect(result, "10"); }); test('tap', () { var mutable = 0; - final main = Effect.succeed(10).tap( + final main = Effect<Null, String, int>.succeed(10).tap( (_) => Effect.functionSucceed(() { mutable += 1; }), ); expect(mutable, 0); - final result = main.runSync(null); + final result = main.runSync(); expect(result, 10); expect(mutable, 1); }); From 24449a3b516136eb9df62816f6e398c9827a3573 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 16:45:06 +0900 Subject: [PATCH 64/91] update examples --- examples/fpdart_http/lib/api.dart | 8 ++++---- examples/fpdart_http/lib/main.dart | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/fpdart_http/lib/api.dart b/examples/fpdart_http/lib/api.dart index 2c7d600..a464fc0 100644 --- a/examples/fpdart_http/lib/api.dart +++ b/examples/fpdart_http/lib/api.dart @@ -10,19 +10,19 @@ Effect<http.Client, HttpError, http.Response> get( }) => /// 2️⃣ Use the Do notation with the `gen` constructor - Effect.gen((_) async { + Effect.gen(($) async { /// 3️⃣ Extract the dependency using `env` (environment) - final client = _.sync(Effect.env()); + final client = $.sync(Effect.env()); /// 4️⃣ Perform a request, catch errors, extract the response - final response = await _.async(Effect.tryCatch( + final response = await $.async(Effect.tryCatch( execute: () => client.get(url, headers: headers), onError: (_, __) => const RequestError(), )); /// 5️⃣ Use plain dart code to check for valid status if (response.statusCode != 200) { - return _.sync(Effect.fail(const ResponseError())); + return $.sync(Effect.fail(const ResponseError())); } /// 6️⃣ Return extracted/valid response diff --git a/examples/fpdart_http/lib/main.dart b/examples/fpdart_http/lib/main.dart index 6542ac4..844bdd8 100644 --- a/examples/fpdart_http/lib/main.dart +++ b/examples/fpdart_http/lib/main.dart @@ -12,7 +12,8 @@ void main() async { () => print(response.body), ), ) - .runFuture( + .provide( http.Client(), - ); + ) + .runFuture(); } From 07bfa82349e58675e7ed811231c645c712562b54 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 16:45:35 +0900 Subject: [PATCH 65/91] update examples (again) --- examples/fpdart_http/lib/main.dart | 2 +- examples/poke_api/lib/main.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/fpdart_http/lib/main.dart b/examples/fpdart_http/lib/main.dart index 844bdd8..5f6bcf2 100644 --- a/examples/fpdart_http/lib/main.dart +++ b/examples/fpdart_http/lib/main.dart @@ -4,7 +4,7 @@ import 'package:http/http.dart' as http; import 'api.dart'; void main() async { - final main = await get( + await get( Uri.https("pokeapi.co", "/api/v2/pokemon/10"), ) .tap( diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index 55e4f2c..cd31499 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -72,7 +72,7 @@ void main() async { () => print("No pokemon: $error"), ), ) - .runFutureExit((Http(), JsonCodec())); + .provide((Http(), JsonCodec())).runFutureExit(); print(exit); } From 3e72bf5a2c92d4150f49de1e1ff08fb7af2086e7 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Mon, 25 Mar 2024 17:17:53 +0900 Subject: [PATCH 66/91] catch error in gen future --- examples/poke_api/lib/main.dart | 6 +++--- .../fpdart/lib/src/extension/future_or_extension.dart | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index cd31499..1320f92 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -7,12 +7,12 @@ import 'package:poke_api/pokemon.dart'; import 'package:poke_api/pokemon_error.dart'; abstract interface class HttpClient { - Effect<E, PokemonError, String> get<E>(Uri uri); + Effect<Null, PokemonError, String> get(Uri uri); } class Http implements HttpClient { @override - Effect<E, PokemonError, String> get<E>(Uri uri) => Effect.gen( + Effect<Null, PokemonError, String> get(Uri uri) => Effect.gen( ($) async { final response = await $.async(Effect.tryCatch( execute: () => http.get(uri), @@ -42,7 +42,7 @@ Effect<Env, PokemonError, Pokemon> program( } final uri = Uri.parse(Constants.requestAPIUrl(id)); - final body = await $.async(client.get(uri)); + final body = await $.async(client.get(uri).withEnv()); final bodyJson = $.sync( Either.tryCatch( diff --git a/packages/fpdart/lib/src/extension/future_or_extension.dart b/packages/fpdart/lib/src/extension/future_or_extension.dart index ef6d307..4224e57 100644 --- a/packages/fpdart/lib/src/extension/future_or_extension.dart +++ b/packages/fpdart/lib/src/extension/future_or_extension.dart @@ -1,13 +1,14 @@ import 'dart:async'; extension FutureOrThenExtension<A> on FutureOr<A> { - FutureOr<B> then<B>(FutureOr<B> Function(A a) f, - {B Function(Object error, StackTrace stackTrace)? onError}) { + FutureOr<B> then<B>( + FutureOr<B> Function(A a) f, { + B Function(Object error, StackTrace stackTrace)? onError, + }) { switch (this) { case Future<A> self: - return self.then( - f, - onError: (Object error, StackTrace stackTrace) { + return self.then(f).catchError( + (Object error, StackTrace stackTrace) { if (onError != null) onError(error, stackTrace); }, ); From 57eb5ee15a82409eebbb2ed16ca103e6907e9cec Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Wed, 27 Mar 2024 06:22:37 +0900 Subject: [PATCH 67/91] effect abstract only --- packages/fpdart/lib/src/effect.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 8d968ca..c8f88e3 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -54,7 +54,7 @@ typedef DoFunctionEffect<E, L, A> = FutureOr<A> Function( EffectGen<E, L> $, ); -abstract interface class IEffect<E, L, R> { +abstract class IEffect<E, L, R> { const IEffect(); Effect<E, L, R> get asEffect; } From 34730962b3c4000fa4ed1da93c830d0c0207451c Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Wed, 27 Mar 2024 18:58:30 +0900 Subject: [PATCH 68/91] `meta` version --- packages/fpdart/lib/src/extension/future_or_extension.dart | 1 + packages/fpdart/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/fpdart/lib/src/extension/future_or_extension.dart b/packages/fpdart/lib/src/extension/future_or_extension.dart index 4224e57..67d3cf8 100644 --- a/packages/fpdart/lib/src/extension/future_or_extension.dart +++ b/packages/fpdart/lib/src/extension/future_or_extension.dart @@ -10,6 +10,7 @@ extension FutureOrThenExtension<A> on FutureOr<A> { return self.then(f).catchError( (Object error, StackTrace stackTrace) { if (onError != null) onError(error, stackTrace); + throw error; }, ); case A self: diff --git a/packages/fpdart/pubspec.yaml b/packages/fpdart/pubspec.yaml index fe0b5fe..b4a2a6d 100644 --- a/packages/fpdart/pubspec.yaml +++ b/packages/fpdart/pubspec.yaml @@ -13,7 +13,7 @@ environment: sdk: ">=3.3.0 <4.0.0" dependencies: - meta: ^1.12.0 + meta: ^1.11.0 dev_dependencies: lints: ^3.0.0 From f343b0314c53a45b8c050294b54fff82f64b9761 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Wed, 27 Mar 2024 19:14:38 +0900 Subject: [PATCH 69/91] run effects with catching --- packages/fpdart/lib/src/effect.dart | 85 ++++++++++--------- .../effect/effect_error_handling_test.dart | 23 +++++ 2 files changed, 70 insertions(+), 38 deletions(-) create mode 100644 packages/fpdart/test/src/effect/effect_error_handling_test.dart diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index c8f88e3..41a0687 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -71,6 +71,18 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { return "Effect(${_unsafeRun.runtimeType})"; } + /// {@category constructors} + factory Effect.from(FutureOr<Exit<L, R>> Function(E env) run) => + Effect._((env) { + try { + return run(env); + } on Cause<L> catch (cause) { + return Left(cause); + } catch (error, stackTrace) { + return Left(Die(error, stackTrace)); + } + }); + /// {@category constructors} factory Effect.gen(DoFunctionEffect<E, L, R> f) => Effect<E, L, R>._( (env) { @@ -97,7 +109,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required L Function(Object error, StackTrace stackTrace) onError, FutureOr<dynamic> Function()? onCancel, }) => - Effect._( + Effect.from( (env) { try { return execute().then( @@ -114,42 +126,39 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category constructors} factory Effect.fromNullable(R? value, {required L Function() onNull}) => - Effect._( + Effect.from( (_) => value == null ? Left(Failure(onNull())) : Right(value), ); /// {@category constructors} - factory Effect.functionFail(FutureOr<Cause<L>> Function() f) => Effect._( + factory Effect.functionFail(FutureOr<Cause<L>> Function() f) => Effect.from( (_) => f().then(Left.new), ); /// {@category constructors} - factory Effect.functionSucceed(FutureOr<R> Function() f) => Effect._( + factory Effect.functionSucceed(FutureOr<R> Function() f) => Effect.from( (_) => f().then(Right.new), ); /// {@category constructors} - factory Effect.from(Exit<L, R> Function(E env) f) => Effect._(f); - - /// {@category constructors} - factory Effect.fail(L value) => Effect._((_) => Left(Failure(value))); + factory Effect.fail(L value) => Effect.from((_) => Left(Failure(value))); /// {@category constructors} - factory Effect.succeed(R value) => Effect._((_) => Right(value)); + factory Effect.succeed(R value) => Effect.from((_) => Right(value)); /// {@category constructors} - static Effect<E, Never, Never> die<E>(dynamic defect) => Effect._( + static Effect<E, Never, Never> die<E>(dynamic defect) => Effect.from( (_) => Left(Die.current(defect)), ); /// {@category constructors} static Effect<E, Never, Never> functionDie<E>(dynamic Function() run) => - Effect._( + Effect.from( (_) => Left(Die.current(run())), ); /// {@category constructors} - static Effect<E, L, fpdart_unit.Unit> unit<E, L>() => Effect._( + static Effect<E, L, fpdart_unit.Unit> unit<E, L>() => Effect.from( (_) => const Right(fpdart_unit.unit), ); @@ -158,7 +167,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Iterable<A> iterable, Effect<E, L, R> Function(A a, int index) f, ) => - Effect._( + Effect.from( (env) { if (iterable.isEmpty) { return const Right([]); @@ -211,15 +220,15 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { flatMap((_) => effect); /// {@category do_notation} - Effect<V, L, R> mapEnv<V>(E Function(V env) f) => Effect._( + Effect<V, L, R> mapEnv<V>(E Function(V env) f) => Effect.from( (env) => _unsafeRun(f(env)), ); /// {@category do_notation} - Effect<Null, L, R> provide(E env) => Effect._((_) => _unsafeRun(env)); + Effect<Null, L, R> provide(E env) => Effect.from((_) => _unsafeRun(env)); /// {@category do_notation} - Effect<V, L, R> provideEffect<V>(Effect<V, L, E> effect) => Effect._( + Effect<V, L, R> provideEffect<V>(Effect<V, L, E> effect) => Effect.from( (env) => effect._unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => Left(cause), @@ -229,7 +238,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category do_notation} - static Effect<E, L, E> env<E, L>() => Effect._( + static Effect<E, L, E> env<E, L>() => Effect.from( (env) => Right(env), ); @@ -244,7 +253,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category conversions} - Effect<E, Never, Either<L, R>> either() => Effect._( + Effect<E, Never, Either<L, R>> either() => Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -257,7 +266,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category conversions} - Effect<E, Never, Option<R>> option() => Effect._( + Effect<E, Never, Option<R>> option() => Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -270,7 +279,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category conversions} - Effect<E, Never, Exit<L, R>> exit() => Effect._( + Effect<E, Never, Exit<L, R>> exit() => Effect.from( (env) => _unsafeRun(env).then( (exit) => Right(exit), ), @@ -281,7 +290,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required C Function(L l) onFailure, required C Function(R r) onSuccess, }) => - Effect._( + Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -298,7 +307,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required C Function(Cause<L> l) onFailure, required C Function(R r) onSuccess, }) => - Effect._( + Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => Right(onFailure(cause)), @@ -312,7 +321,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required Effect<E, C, D> Function(L l) onFailure, required Effect<E, C, D> Function(R r) onSuccess, }) => - Effect._( + Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -330,7 +339,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required Effect<E, C, D> Function(Cause<L> l) onFailure, required Effect<E, C, D> Function(R r) onSuccess, }) => - Effect._( + Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => onFailure(cause)._unsafeRun(env), @@ -340,7 +349,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category mapping} - Effect<E, R, L> flip() => Effect._( + Effect<E, R, L> flip() => Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -356,7 +365,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, L, V> map<V>(V Function(R r) f) => ap(Effect.succeed(f)); /// {@category mapping} - Effect<E, C, R> mapError<C>(C Function(L l) f) => Effect._( + Effect<E, C, R> mapError<C>(C Function(L l) f) => Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -369,7 +378,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category mapping} - Effect<E, C, R> mapErrorCause<C>(C Function(Cause<L> l) f) => Effect._( + Effect<E, C, R> mapErrorCause<C>(C Function(Cause<L> l) f) => Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => Left(Failure(f(cause))), @@ -380,7 +389,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category mapping} Effect<E, C, D> mapBoth<C, D>(C Function(L l) fl, D Function(R r) fr) => - Effect._( + Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -393,7 +402,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category sequencing} - Effect<E, L, C> flatMap<C>(Effect<E, L, C> Function(R r) f) => Effect._( + Effect<E, L, C> flatMap<C>(Effect<E, L, C> Function(R r) f) => Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => Left(cause), @@ -407,7 +416,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { flatMap((r) => f(r).map((_) => r)); /// {@category sequencing} - Effect<E, L, R> tapError<C>(Effect<E, C, R> Function(L l) f) => Effect._( + Effect<E, L, R> tapError<C>(Effect<E, C, R> Function(L l) f) => Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -425,7 +434,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, C, R> orElse<C>( Effect<E, C, R> Function(L l) orElse, ) => - Effect._( + Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -439,7 +448,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category alternatives} - Effect<E, Never, R> get orDie => Effect._( + Effect<E, Never, R> get orDie => Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => Left(Die.current(cause)), @@ -451,7 +460,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category alternatives} Effect<E, Never, R> orDieWith<T extends Object>(T Function(L l) onError) => - Effect._( + Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -469,7 +478,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, C, R> catchError<C>( Effect<E, C, R> Function(L error) f, ) => - Effect._( + Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -486,7 +495,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, C, R> catchCause<C>( Effect<E, C, R> Function(Cause<L> cause) f, ) => - Effect._( + Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => f(cause), @@ -501,7 +510,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required bool Function(R r) predicate, required C Function(R r) orDieWith, }) => - Effect._( + Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => Left(cause), @@ -517,7 +526,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required bool Function(R r) predicate, required Effect<E, L, R> Function(R r) orElse, }) => - Effect._( + Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => Left(cause), @@ -530,7 +539,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { extension ProvideNull<L, R> on Effect<Null, L, R> { /// {@category do_notation} - Effect<V, L, R> withEnv<V>() => Effect._( + Effect<V, L, R> withEnv<V>() => Effect.from( (env) => _unsafeRun(null), ); diff --git a/packages/fpdart/test/src/effect/effect_error_handling_test.dart b/packages/fpdart/test/src/effect/effect_error_handling_test.dart new file mode 100644 index 0000000..86ca8de --- /dev/null +++ b/packages/fpdart/test/src/effect/effect_error_handling_test.dart @@ -0,0 +1,23 @@ +import "package:fpdart/fpdart.dart"; +import "package:test/test.dart"; + +void main() { + group( + "Effect error handling", + () { + group('catchCause', () { + test('recover from throw', () { + final result = Effect<Null, Never, String>.functionSucceed(() { + throw "fail"; + }) + .catchCause( + (cause) => Effect.succeed("abc"), + ) + .runSync(); + + expect(result, "abc"); + }); + }); + }, + ); +} From 83667b4c3993ada1655622a4f8aec008fc40e734 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 28 Mar 2024 06:13:13 +0900 Subject: [PATCH 70/91] `Deferred` and `Context` --- examples/poke_api/analysis_options.yaml | 1 + packages/fpdart/lib/src/context.dart | 31 +++ packages/fpdart/lib/src/deferred.dart | 51 +++++ packages/fpdart/lib/src/effect.dart | 178 ++++++++++++------ packages/fpdart/lib/src/exit.dart | 18 ++ .../src/effect/effect_do_notation_test.dart | 7 +- 6 files changed, 222 insertions(+), 64 deletions(-) create mode 100644 packages/fpdart/lib/src/context.dart create mode 100644 packages/fpdart/lib/src/deferred.dart diff --git a/examples/poke_api/analysis_options.yaml b/examples/poke_api/analysis_options.yaml index d875ed0..027c612 100644 --- a/examples/poke_api/analysis_options.yaml +++ b/examples/poke_api/analysis_options.yaml @@ -3,6 +3,7 @@ include: package:lints/recommended.yaml linter: rules: annotate_overrides: true + prefer_void_to_null: false analyzer: language: diff --git a/packages/fpdart/lib/src/context.dart b/packages/fpdart/lib/src/context.dart new file mode 100644 index 0000000..fd7d41b --- /dev/null +++ b/packages/fpdart/lib/src/context.dart @@ -0,0 +1,31 @@ +part of "effect.dart"; + +final class Context<E> { + final E env; + final Deferred<Never, Never> signal; + + const Context._({required this.env, required this.signal}); + + factory Context({ + required E env, + required Deferred<Never, Never> signal, + }) => + Context._(env: env, signal: signal); + + factory Context.env(E env) => Context._(env: env, signal: Deferred()); + + Context<C> withEnv<C>(C env) => Context._(env: env, signal: signal); + + Context<E> get withoutSignal => withSignal(Deferred()); + Context<E> withSignal(Deferred<Never, Never> signal) => + copyWith(signal: signal); + + Context<E> copyWith({ + E? env, + Deferred<Never, Never>? signal, + }) => + Context._( + env: env ?? this.env, + signal: signal ?? this.signal, + ); +} diff --git a/packages/fpdart/lib/src/deferred.dart b/packages/fpdart/lib/src/deferred.dart new file mode 100644 index 0000000..9ccb12d --- /dev/null +++ b/packages/fpdart/lib/src/deferred.dart @@ -0,0 +1,51 @@ +part of 'effect.dart'; + +final class Deferred<L, R> extends IEffect<Null, L, R> { + Option<Exit<L, R>> _value = None(); + + Completer<Exit<L, R>>? __completer; + + Deferred(); + + Deferred.completed(R value) : _value = Some(Right(value)); + Deferred.failedCause(Cause<L> cause) : _value = Some(Left(cause)); + Deferred.failed(L error) : _value = Some(Left(Failure(error))); + + Completer<Exit<L, R>> get _completer => + __completer ??= Completer<Exit<L, R>>.sync(); + + bool get unsafeCompleted => _value is Some<Exit<L, R>>; + + @override + Effect<Null, L, R> get asEffect => Effect.from( + (_) => await<Null>()._unsafeRun(Context.env(null)), + ); + + Effect<E, L, R> await<E>() => Effect.from( + (ctx) async => switch (_value) { + None() => await _completer.future, + Some(value: final value) => value, + }, + ); + + void unsafeCompleteExit(Exit<L, R> exit) { + if (_value is Some<Exit<L, R>>) return; + + _value = Some(exit); + __completer?.complete(exit); + } + + Effect<E, C, Unit> completeExit<E, C>(Exit<L, R> exit) => + Effect.functionSucceed(() { + switch (_value) { + case None(): + unsafeCompleteExit(exit); + return unit; + case Some(): + return unit; + } + }); + + Effect<E, C, Unit> failCause<E, C>(Cause<L> cause) => + completeExit(Left(cause)); +} diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 41a0687..cd7e3c5 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -6,6 +6,8 @@ import './extension/future_or_extension.dart'; import './extension/iterable_extension.dart'; import 'unit.dart' as fpdart_unit; +part 'context.dart'; +part 'deferred.dart'; part 'either.dart'; part 'option.dart'; @@ -24,9 +26,9 @@ final class _EffectThrow<L> implements Exception { } } -EffectGen<E, L> _effectGen<E, L>(E env) => ( +EffectGen<E, L> _effectGen<E, L>(Context<E> context) => ( async: <A>(effect) => Future.sync( - () => effect.asEffect._unsafeRun(env).then( + () => effect.asEffect._unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => throw _EffectThrow<L>(cause), Right(value: final value) => value, @@ -34,7 +36,7 @@ EffectGen<E, L> _effectGen<E, L>(E env) => ( ), ), sync: <A>(effect) { - final run = effect.asEffect._unsafeRun(env); + final run = effect.asEffect._unsafeRun(context); if (run is Future) { throw _EffectThrow<L>( Die.current( @@ -60,7 +62,7 @@ abstract class IEffect<E, L, R> { } final class Effect<E, L, R> extends IEffect<E, L, R> { - final FutureOr<Exit<L, R>> Function(E env) _unsafeRun; + final FutureOr<Exit<L, R>> Function(Context<E> context) _unsafeRun; const Effect._(this._unsafeRun); @override @@ -71,11 +73,29 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { return "Effect(${_unsafeRun.runtimeType})"; } + FutureOr<Exit<L, R>> __unsafeRun(Context<E> context) { + if (context.signal.unsafeCompleted) { + return const Left(Interrupted()); + } + + try { + return _unsafeRun(context).then((exit) { + if (context.signal.unsafeCompleted) { + return const Left(Interrupted()); + } + + return exit; + }); + } catch (err, stackTrace) { + return Left(Die(err, stackTrace)); + } + } + /// {@category constructors} - factory Effect.from(FutureOr<Exit<L, R>> Function(E env) run) => - Effect._((env) { + factory Effect.from(FutureOr<Exit<L, R>> Function(Context<E> context) run) => + Effect._((context) { try { - return run(env); + return run(context); } on Cause<L> catch (cause) { return Left(cause); } catch (error, stackTrace) { @@ -146,6 +166,30 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category constructors} factory Effect.succeed(R value) => Effect.from((_) => Right(value)); + /// {@category constructors} + factory Effect.raceAll(Iterable<Effect<E, L, R>> iterable) => + Effect.from((context) { + final signal = Deferred<Never, Never>(); + final deferred = Deferred<L, R>(); + + for (final effect in iterable) { + effect + .__unsafeRun(context.withSignal(signal)) + .then(deferred.unsafeCompleteExit); + + if (deferred.unsafeCompleted) { + break; + } + } + + return deferred.await<E>().__unsafeRun(context).then( + (exit) => signal + .failCause<E, L>(const Interrupted()) + .__unsafeRun(context.withoutSignal) + .then((_) => exit), + ); + }); + /// {@category constructors} static Effect<E, Never, Never> die<E>(dynamic defect) => Effect.from( (_) => Left(Die.current(defect)), @@ -168,7 +212,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, L, R> Function(A a, int index) f, ) => Effect.from( - (env) { + (context) { if (iterable.isEmpty) { return const Right([]); } @@ -182,7 +226,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (list, r) => list.append(r), ), ) - ._unsafeRun(env); + ._unsafeRun(context); }, ); @@ -220,26 +264,28 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { flatMap((_) => effect); /// {@category do_notation} - Effect<V, L, R> mapEnv<V>(E Function(V env) f) => Effect.from( - (env) => _unsafeRun(f(env)), + Effect<V, L, R> mapEnv<V>(Context<E> Function(Context<V> context) f) => + Effect.from( + (context) => _unsafeRun(f(context)), ); /// {@category do_notation} - Effect<Null, L, R> provide(E env) => Effect.from((_) => _unsafeRun(env)); + Effect<Null, L, R> provide(E env) => + Effect.from((_) => _unsafeRun(Context.env(env))); /// {@category do_notation} Effect<V, L, R> provideEffect<V>(Effect<V, L, E> effect) => Effect.from( - (env) => effect._unsafeRun(env).then( + (context) => effect._unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => Left(cause), - Right(value: final value) => _unsafeRun(value), + Right(value: final value) => _unsafeRun(context.withEnv(value)), }, ), ); /// {@category do_notation} static Effect<E, L, E> env<E, L>() => Effect.from( - (env) => Right(env), + (context) => Right(context.env), ); /// {@category combining} @@ -254,11 +300,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category conversions} Effect<E, Never, Either<L, R>> either() => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure<L>(error: final error) => Right(Left(error)), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => Right(Right(value)), }, @@ -267,11 +314,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category conversions} Effect<E, Never, Option<R>> option() => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure<L>() => Right(None()), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => Right(Some(value)), }, @@ -280,7 +328,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category conversions} Effect<E, Never, Exit<L, R>> exit() => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => Right(exit), ), ); @@ -291,11 +339,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required C Function(R r) onSuccess, }) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure<L>(error: final error) => Right(onFailure(error)), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => Right(onSuccess(value)), }, @@ -308,7 +357,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required C Function(R r) onSuccess, }) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => Right(onFailure(cause)), Right(value: final value) => Right(onSuccess(value)), @@ -322,14 +371,15 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required Effect<E, C, D> Function(R r) onSuccess, }) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure<L>(error: final error) => - onFailure(error)._unsafeRun(env), + onFailure(error)._unsafeRun(context), Die() => Left(cause), + Interrupted() => Left(cause), }, - Right(value: final value) => onSuccess(value)._unsafeRun(env), + Right(value: final value) => onSuccess(value)._unsafeRun(context), }, ), ); @@ -340,21 +390,22 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required Effect<E, C, D> Function(R r) onSuccess, }) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { - Left(value: final cause) => onFailure(cause)._unsafeRun(env), - Right(value: final value) => onSuccess(value)._unsafeRun(env), + Left(value: final cause) => onFailure(cause)._unsafeRun(context), + Right(value: final value) => onSuccess(value)._unsafeRun(context), }, ), ); /// {@category mapping} Effect<E, R, L> flip() => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure<L>(error: final error) => Right(error), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => Left(Failure(value)), }, @@ -366,11 +417,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category mapping} Effect<E, C, R> mapError<C>(C Function(L l) f) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure<L>(error: final error) => Left(Failure(f(error))), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => Right(value), }, @@ -379,7 +431,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category mapping} Effect<E, C, R> mapErrorCause<C>(C Function(Cause<L> l) f) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => Left(Failure(f(cause))), Right(value: final value) => Right(value), @@ -390,11 +442,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category mapping} Effect<E, C, D> mapBoth<C, D>(C Function(L l) fl, D Function(R r) fr) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure<L>(error: final error) => Left(Failure(fl(error))), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => Right(fr(value)), }, @@ -403,10 +456,10 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category sequencing} Effect<E, L, C> flatMap<C>(Effect<E, L, C> Function(R r) f) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => Left(cause), - Right(value: final value) => f(value)._unsafeRun(env), + Right(value: final value) => f(value)._unsafeRun(context), }, ), ); @@ -417,13 +470,15 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category sequencing} Effect<E, L, R> tapError<C>(Effect<E, C, R> Function(L l) f) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Failure<L>(error: final error) => f(error)._unsafeRun(env).then( - (_) => Left(Failure(error)), - ), + Failure<L>(error: final error) => + f(error)._unsafeRun(context).then( + (_) => Left(Failure(error)), + ), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => Right(value), }, @@ -435,25 +490,27 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, C, R> Function(L l) orElse, ) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Failure<L>(error: final error) => orElse(error)._unsafeRun(env), + Failure<L>(error: final error) => + orElse(error)._unsafeRun(context), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => - Effect<E, C, R>.succeed(value)._unsafeRun(env), + Effect<E, C, R>.succeed(value)._unsafeRun(context), }, ), ); /// {@category alternatives} Effect<E, Never, R> get orDie => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => Left(Die.current(cause)), Right(value: final value) => - Effect<E, Never, R>.succeed(value)._unsafeRun(env), + Effect<E, Never, R>.succeed(value)._unsafeRun(context), }, ), ); @@ -461,15 +518,16 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category alternatives} Effect<E, Never, R> orDieWith<T extends Object>(T Function(L l) onError) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure<L>(error: final error) => Left(Die.current(onError(error))), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => - Effect<E, Never, R>.succeed(value)._unsafeRun(env), + Effect<E, Never, R>.succeed(value)._unsafeRun(context), }, ), ); @@ -479,14 +537,15 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, C, R> Function(L error) f, ) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Failure<L>(error: final error) => f(error)._unsafeRun(env), + Failure<L>(error: final error) => f(error)._unsafeRun(context), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => - Effect<E, Never, R>.succeed(value)._unsafeRun(env), + Effect<E, Never, R>.succeed(value)._unsafeRun(context), }, ), ); @@ -496,12 +555,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, C, R> Function(Cause<L> cause) f, ) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => f(cause), Right(value: final value) => Effect<E, C, R>.succeed(value), } - ._unsafeRun(env), + ._unsafeRun(context), ), ); @@ -511,7 +570,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required C Function(R r) orDieWith, }) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => Left(cause), Right(value: final value) => predicate(value) @@ -527,11 +586,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { required Effect<E, L, R> Function(R r) orElse, }) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => Left(cause), - Right(value: final value) => - predicate(value) ? Right(value) : orElse(value)._unsafeRun(env), + Right(value: final value) => predicate(value) + ? Right(value) + : orElse(value)._unsafeRun(context), }, ), ); @@ -540,13 +600,13 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { extension ProvideNull<L, R> on Effect<Null, L, R> { /// {@category do_notation} Effect<V, L, R> withEnv<V>() => Effect.from( - (env) => _unsafeRun(null), + (context) => _unsafeRun(Context.env(null)), ); /// {@category execution} R runSync() { try { - final result = _unsafeRun(null); + final result = _unsafeRun(Context.env(null)); if (result is Future) { throw Die.current( Exception("runSync cannot execute async Effect"), @@ -567,7 +627,7 @@ extension ProvideNull<L, R> on Effect<Null, L, R> { /// {@category execution} Exit<L, R> runSyncExit() { try { - final result = _unsafeRun(null); + final result = _unsafeRun(Context.env(null)); if (result is Future) { return Left(Die.current( Exception("runSyncExit cannot execute async Effect"), @@ -584,7 +644,7 @@ extension ProvideNull<L, R> on Effect<Null, L, R> { /// {@category execution} Future<R> runFuture() async { try { - final result = _unsafeRun(null); + final result = _unsafeRun(Context.env(null)); if (result is! Future) { return switch (result) { Left(value: final cause) => throw cause, @@ -606,11 +666,7 @@ extension ProvideNull<L, R> on Effect<Null, L, R> { /// {@category execution} Future<Exit<L, R>> runFutureExit() async { try { - final result = _unsafeRun(null); - if (result is! Future) { - return result; - } - return result; + return _unsafeRun(Context.env(null)); } on Cause<L> catch (cause) { return Left(cause); } catch (error, stackTrace) { diff --git a/packages/fpdart/lib/src/exit.dart b/packages/fpdart/lib/src/exit.dart index b646142..6e6eb10 100644 --- a/packages/fpdart/lib/src/exit.dart +++ b/packages/fpdart/lib/src/exit.dart @@ -51,3 +51,21 @@ final class Failure<L> extends Cause<L> { return "Cause.Fail($error)"; } } + +final class Interrupted extends Cause<Never> { + @override + final StackTrace? stackTrace; + + const Interrupted([this.stackTrace]); + + @override + bool operator ==(Object other) => (other is Interrupted); + + @override + int get hashCode => 0; + + @override + String toString() { + return "Cause.Interrupted()"; + } +} diff --git a/packages/fpdart/test/src/effect/effect_do_notation_test.dart b/packages/fpdart/test/src/effect/effect_do_notation_test.dart index d3ff7d1..bdacf57 100644 --- a/packages/fpdart/test/src/effect/effect_do_notation_test.dart +++ b/packages/fpdart/test/src/effect/effect_do_notation_test.dart @@ -46,10 +46,11 @@ void main() { group('mapEnv', () { test('adapt dependency from another program', () { - final subMain = - Effect<int, String, int>.from((env) => Right(env + 1)); + final subMain = Effect<int, String, int>.from( + (context) => Right(context.env + 1)); final main = Effect<String, String, int>.gen(($) { - final value = $.sync(subMain.mapEnv((env) => env.length)); + final value = $.sync( + subMain.mapEnv((context) => Context.env(context.env.length))); return value; }); From b79f034b8a7cf314e0e9c2caf7a5ae440b5db85a Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 28 Mar 2024 06:25:21 +0900 Subject: [PATCH 71/91] `race` and `raceAll` --- packages/fpdart/lib/src/effect.dart | 12 ++++++++++ .../src/effect/effect_sequencing_test.dart | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index cd7e3c5..b43893f 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -190,6 +190,14 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); }); + /// {@category constructors} + static Effect<E, Never, void> sleep<E>(Duration duration) => Effect.from( + (_) => Future.delayed( + duration, + () => const Right(null), + ), + ); + /// {@category constructors} static Effect<E, Never, Never> die<E>(dynamic defect) => Effect.from( (_) => Left(Die.current(defect)), @@ -454,6 +462,10 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); + /// {@category sequencing} + Effect<E, L, R> race(Effect<E, L, R> effect) => + Effect.raceAll([this, effect]); + /// {@category sequencing} Effect<E, L, C> flatMap<C>(Effect<E, L, C> Function(R r) f) => Effect.from( (context) => _unsafeRun(context).then( diff --git a/packages/fpdart/test/src/effect/effect_sequencing_test.dart b/packages/fpdart/test/src/effect/effect_sequencing_test.dart index ee6c273..75d711d 100644 --- a/packages/fpdart/test/src/effect/effect_sequencing_test.dart +++ b/packages/fpdart/test/src/effect/effect_sequencing_test.dart @@ -32,6 +32,28 @@ void main() { expect(result, 10); expect(mutable, 1); }); + + group('race', () { + test('first wins', () async { + final first = + Effect.sleep<Null>(Duration(milliseconds: 50)).map((_) => 1); + final second = + Effect.sleep<Null>(Duration(milliseconds: 100)).map((_) => 2); + + final result = await first.race(second).runFuture(); + expect(result, 1); + }); + + test('second wins', () async { + final first = + Effect.sleep<Null>(Duration(milliseconds: 100)).map((_) => 1); + final second = + Effect.sleep<Null>(Duration(milliseconds: 50)).map((_) => 2); + + final result = await first.race(second).runFuture(); + expect(result, 2); + }); + }); }, ); } From e4d3ad2a0446ee1f79498fda8066db6310493eb7 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 28 Mar 2024 06:30:57 +0900 Subject: [PATCH 72/91] `delay` and `timeout` --- packages/fpdart/lib/src/effect.dart | 13 ++++++++++++- .../test/src/effect/effect_sequencing_test.dart | 16 ++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index b43893f..fb22f2d 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -163,6 +163,9 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category constructors} factory Effect.fail(L value) => Effect.from((_) => Left(Failure(value))); + /// {@category constructors} + factory Effect.failCause(Cause<L> cause) => Effect.from((_) => Left(cause)); + /// {@category constructors} factory Effect.succeed(R value) => Effect.from((_) => Right(value)); @@ -191,7 +194,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { }); /// {@category constructors} - static Effect<E, Never, void> sleep<E>(Duration duration) => Effect.from( + static Effect<E, L, void> sleep<E, L>(Duration duration) => Effect.from( (_) => Future.delayed( duration, () => const Right(null), @@ -607,6 +610,14 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { }, ), ); + + /// {@category delay} + Effect<E, L, R> delay(Duration duration) => + Effect.sleep<E, L>(duration).zipRight(this); + + /// {@category delay} + Effect<E, L, R> timeout(Duration duration) => + race(Effect<E, L, R>.failCause(const Interrupted()).delay(duration)); } extension ProvideNull<L, R> on Effect<Null, L, R> { diff --git a/packages/fpdart/test/src/effect/effect_sequencing_test.dart b/packages/fpdart/test/src/effect/effect_sequencing_test.dart index 75d711d..8d92543 100644 --- a/packages/fpdart/test/src/effect/effect_sequencing_test.dart +++ b/packages/fpdart/test/src/effect/effect_sequencing_test.dart @@ -35,20 +35,20 @@ void main() { group('race', () { test('first wins', () async { - final first = - Effect.sleep<Null>(Duration(milliseconds: 50)).map((_) => 1); - final second = - Effect.sleep<Null>(Duration(milliseconds: 100)).map((_) => 2); + final first = Effect.sleep<Null, String>(Duration(milliseconds: 50)) + .map((_) => 1); + final second = Effect.sleep<Null, String>(Duration(milliseconds: 100)) + .map((_) => 2); final result = await first.race(second).runFuture(); expect(result, 1); }); test('second wins', () async { - final first = - Effect.sleep<Null>(Duration(milliseconds: 100)).map((_) => 1); - final second = - Effect.sleep<Null>(Duration(milliseconds: 50)).map((_) => 2); + final first = Effect.sleep<Null, String>(Duration(milliseconds: 100)) + .map((_) => 1); + final second = Effect.sleep<Null, String>(Duration(milliseconds: 50)) + .map((_) => 2); final result = await first.race(second).runFuture(); expect(result, 2); From ac7f9e10416356e4fd0edc33f5584f60dfaa6baf Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 28 Mar 2024 06:44:48 +0900 Subject: [PATCH 73/91] `Deferred` as `Effect` --- packages/fpdart/example/main.dart | 1 + packages/fpdart/lib/src/deferred.dart | 12 +++++++++--- packages/fpdart/lib/src/effect.dart | 3 +++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/fpdart/example/main.dart b/packages/fpdart/example/main.dart index 8391106..a73ba91 100644 --- a/packages/fpdart/example/main.dart +++ b/packages/fpdart/example/main.dart @@ -10,5 +10,6 @@ final option = Some(10); final effect = Effect<Env, Error, Success>.gen(($) { final eitherValue = $.sync(either); final optionValue = $.sync(option); + final deferred = $.sync(Deferred<Error, Success>()); return eitherValue + optionValue; }); diff --git a/packages/fpdart/lib/src/deferred.dart b/packages/fpdart/lib/src/deferred.dart index 9ccb12d..43b2e42 100644 --- a/packages/fpdart/lib/src/deferred.dart +++ b/packages/fpdart/lib/src/deferred.dart @@ -1,6 +1,6 @@ part of 'effect.dart'; -final class Deferred<L, R> extends IEffect<Null, L, R> { +final class Deferred<L, R> extends IEffect<Never, L, Option<R>> { Option<Exit<L, R>> _value = None(); Completer<Exit<L, R>>? __completer; @@ -17,8 +17,14 @@ final class Deferred<L, R> extends IEffect<Null, L, R> { bool get unsafeCompleted => _value is Some<Exit<L, R>>; @override - Effect<Null, L, R> get asEffect => Effect.from( - (_) => await<Null>()._unsafeRun(Context.env(null)), + Effect<Never, L, Option<R>> get asEffect => Effect.from( + (_) => switch (_value) { + None() => Right(None()), + Some(value: final exit) => switch (exit) { + Left() => Right(None()), + Right(value: final value) => Right(Some(value)), + }, + }, ); Effect<E, L, R> await<E>() => Effect.from( diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index fb22f2d..e4f3ff3 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -618,6 +618,9 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category delay} Effect<E, L, R> timeout(Duration duration) => race(Effect<E, L, R>.failCause(const Interrupted()).delay(duration)); + + /// {@category interruption} + Effect<E, Never, R> interrupt() => Effect.failCause(const Interrupted()); } extension ProvideNull<L, R> on Effect<Null, L, R> { From da0b2ee77426e7b4c8616109bfea7dc9b3575c0f Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 29 Mar 2024 05:35:39 +0900 Subject: [PATCH 74/91] `Deferred` effect with test --- packages/fpdart/example/main.dart | 5 ++- packages/fpdart/lib/src/context.dart | 5 ++- packages/fpdart/lib/src/deferred.dart | 40 ++++++++----------- packages/fpdart/lib/src/effect.dart | 6 +-- .../fpdart/test/src/effect/deferred_test.dart | 26 ++++++++++++ 5 files changed, 51 insertions(+), 31 deletions(-) create mode 100644 packages/fpdart/test/src/effect/deferred_test.dart diff --git a/packages/fpdart/example/main.dart b/packages/fpdart/example/main.dart index a73ba91..f5126f5 100644 --- a/packages/fpdart/example/main.dart +++ b/packages/fpdart/example/main.dart @@ -7,9 +7,10 @@ typedef Success = int; final either = Right<Error, Success>(10); final option = Some(10); -final effect = Effect<Env, Error, Success>.gen(($) { +final effect = Effect<Env, Error, Success>.gen(($) async { final eitherValue = $.sync(either); final optionValue = $.sync(option); - final deferred = $.sync(Deferred<Error, Success>()); + final deferred = $.sync(Deferred.make<Error, Success>().withEnv()); + final value = await $.async(deferred.future()); return eitherValue + optionValue; }); diff --git a/packages/fpdart/lib/src/context.dart b/packages/fpdart/lib/src/context.dart index fd7d41b..93e543a 100644 --- a/packages/fpdart/lib/src/context.dart +++ b/packages/fpdart/lib/src/context.dart @@ -12,11 +12,12 @@ final class Context<E> { }) => Context._(env: env, signal: signal); - factory Context.env(E env) => Context._(env: env, signal: Deferred()); + factory Context.env(E env) => + Context._(env: env, signal: Deferred.unsafeMake()); Context<C> withEnv<C>(C env) => Context._(env: env, signal: signal); - Context<E> get withoutSignal => withSignal(Deferred()); + Context<E> get withoutSignal => withSignal(Deferred.unsafeMake()); Context<E> withSignal(Deferred<Never, Never> signal) => copyWith(signal: signal); diff --git a/packages/fpdart/lib/src/deferred.dart b/packages/fpdart/lib/src/deferred.dart index 43b2e42..9ed7335 100644 --- a/packages/fpdart/lib/src/deferred.dart +++ b/packages/fpdart/lib/src/deferred.dart @@ -1,49 +1,41 @@ part of 'effect.dart'; -final class Deferred<L, R> extends IEffect<Never, L, Option<R>> { - Option<Exit<L, R>> _value = None(); - +final class Deferred<L, R> { + Option<Exit<L, R>> _state = None(); Completer<Exit<L, R>>? __completer; - Deferred(); + Deferred._(); + Deferred.unsafeMake(); + + static Effect<Null, Never, Deferred<L, R>> make<L, R>() => + Effect.succeed(Deferred._()); - Deferred.completed(R value) : _value = Some(Right(value)); - Deferred.failedCause(Cause<L> cause) : _value = Some(Left(cause)); - Deferred.failed(L error) : _value = Some(Left(Failure(error))); + Deferred.completed(R value) : _state = Some(Right(value)); + Deferred.failedCause(Cause<L> cause) : _state = Some(Left(cause)); + Deferred.failed(L error) : _state = Some(Left(Failure(error))); Completer<Exit<L, R>> get _completer => __completer ??= Completer<Exit<L, R>>.sync(); - bool get unsafeCompleted => _value is Some<Exit<L, R>>; - - @override - Effect<Never, L, Option<R>> get asEffect => Effect.from( - (_) => switch (_value) { - None() => Right(None()), - Some(value: final exit) => switch (exit) { - Left() => Right(None()), - Right(value: final value) => Right(Some(value)), - }, - }, - ); + bool get unsafeCompleted => _state is Some<Exit<L, R>>; - Effect<E, L, R> await<E>() => Effect.from( - (ctx) async => switch (_value) { + Effect<E, L, R> future<E>() => Effect.from( + (ctx) async => switch (_state) { None() => await _completer.future, Some(value: final value) => value, }, ); void unsafeCompleteExit(Exit<L, R> exit) { - if (_value is Some<Exit<L, R>>) return; + if (_state is Some<Exit<L, R>>) return; - _value = Some(exit); + _state = Some(exit); __completer?.complete(exit); } Effect<E, C, Unit> completeExit<E, C>(Exit<L, R> exit) => Effect.functionSucceed(() { - switch (_value) { + switch (_state) { case None(): unsafeCompleteExit(exit); return unit; diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index e4f3ff3..4a0e130 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -172,8 +172,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category constructors} factory Effect.raceAll(Iterable<Effect<E, L, R>> iterable) => Effect.from((context) { - final signal = Deferred<Never, Never>(); - final deferred = Deferred<L, R>(); + final signal = Deferred<Never, Never>.unsafeMake(); + final deferred = Deferred<L, R>.unsafeMake(); for (final effect in iterable) { effect @@ -185,7 +185,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { } } - return deferred.await<E>().__unsafeRun(context).then( + return deferred.future<E>().__unsafeRun(context).then( (exit) => signal .failCause<E, L>(const Interrupted()) .__unsafeRun(context.withoutSignal) diff --git a/packages/fpdart/test/src/effect/deferred_test.dart b/packages/fpdart/test/src/effect/deferred_test.dart new file mode 100644 index 0000000..9603600 --- /dev/null +++ b/packages/fpdart/test/src/effect/deferred_test.dart @@ -0,0 +1,26 @@ +import 'package:fpdart/fpdart.dart'; +import 'package:test/test.dart'; + +void main() { + group('Deferred', () { + group('future', () { + test('suspends and awaits future', () async { + final main = Effect<Null, String, int>.gen(($) async { + final deferred = $.sync(Deferred.make<String, int>()); + await $.async(Effect.raceAll([ + Effect.sleep<Null, String>(Duration(milliseconds: 100)) + .zipRight(deferred.completeExit(Right(1))), + Effect.sleep<Null, String>(Duration(milliseconds: 150)) + .zipRight(deferred.completeExit(Right(2))), + ])); + + final value = await $.async(deferred.future()); + return value; + }); + + final result = await main.runFuture(); + expect(result, 1); + }); + }); + }); +} From 9c8610cbe97488cfb30ab7adbaf18b94684aecb7 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 29 Mar 2024 05:45:24 +0900 Subject: [PATCH 75/91] interruption test --- .../src/effect/effect_interruption_test.dart | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 packages/fpdart/test/src/effect/effect_interruption_test.dart diff --git a/packages/fpdart/test/src/effect/effect_interruption_test.dart b/packages/fpdart/test/src/effect/effect_interruption_test.dart new file mode 100644 index 0000000..7855a3f --- /dev/null +++ b/packages/fpdart/test/src/effect/effect_interruption_test.dart @@ -0,0 +1,25 @@ +import "package:fpdart/fpdart.dart"; +import "package:test/test.dart"; + +void main() { + group( + "Effect interruption", + () { + group('interrupt', () { + test('fail with Cause.Interrupted', () { + final main = Effect<Null, String, int>.succeed(10).interrupt().map( + (r) => r + 10, + ); + + final result = main.runSyncExit(); + switch (result) { + case Right(): + fail("Either expected to be Left: $result"); + case Left(value: final value): + expect(value, isA<Interrupted>()); + } + }); + }); + }, + ); +} From 4c893ef6d567a7099df74b8311e5b798b257050a Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 29 Mar 2024 06:13:05 +0900 Subject: [PATCH 76/91] provide API --- examples/fpdart_http/lib/main.dart | 3 ++- examples/poke_api/lib/main.dart | 2 +- packages/fpdart/example/main.dart | 2 +- packages/fpdart/lib/src/deferred.dart | 2 +- packages/fpdart/lib/src/effect.dart | 27 +++++++++++++------ .../fpdart/test/src/effect/deferred_test.dart | 2 +- .../src/effect/effect_do_notation_test.dart | 8 +++--- 7 files changed, 29 insertions(+), 17 deletions(-) diff --git a/examples/fpdart_http/lib/main.dart b/examples/fpdart_http/lib/main.dart index 5f6bcf2..9257f0a 100644 --- a/examples/fpdart_http/lib/main.dart +++ b/examples/fpdart_http/lib/main.dart @@ -7,12 +7,13 @@ void main() async { await get( Uri.https("pokeapi.co", "/api/v2/pokemon/10"), ) + .timeout(Duration(milliseconds: 1300)) .tap( (response) => Effect.functionSucceed( () => print(response.body), ), ) - .provide( + .provideEnv( http.Client(), ) .runFuture(); diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index 1320f92..090e748 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -72,7 +72,7 @@ void main() async { () => print("No pokemon: $error"), ), ) - .provide((Http(), JsonCodec())).runFutureExit(); + .provideEnv((Http(), JsonCodec())).runFutureExit(); print(exit); } diff --git a/packages/fpdart/example/main.dart b/packages/fpdart/example/main.dart index f5126f5..c93f5f8 100644 --- a/packages/fpdart/example/main.dart +++ b/packages/fpdart/example/main.dart @@ -11,6 +11,6 @@ final effect = Effect<Env, Error, Success>.gen(($) async { final eitherValue = $.sync(either); final optionValue = $.sync(option); final deferred = $.sync(Deferred.make<Error, Success>().withEnv()); - final value = await $.async(deferred.future()); + final value = await $.async(deferred.wait()); return eitherValue + optionValue; }); diff --git a/packages/fpdart/lib/src/deferred.dart b/packages/fpdart/lib/src/deferred.dart index 9ed7335..c4c5dc9 100644 --- a/packages/fpdart/lib/src/deferred.dart +++ b/packages/fpdart/lib/src/deferred.dart @@ -19,7 +19,7 @@ final class Deferred<L, R> { bool get unsafeCompleted => _state is Some<Exit<L, R>>; - Effect<E, L, R> future<E>() => Effect.from( + Effect<E, L, R> wait<E>() => Effect.from( (ctx) async => switch (_state) { None() => await _completer.future, Some(value: final value) => value, diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 4a0e130..2af3324 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -185,7 +185,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { } } - return deferred.future<E>().__unsafeRun(context).then( + return deferred.wait<E>().__unsafeRun(context).then( (exit) => signal .failCause<E, L>(const Interrupted()) .__unsafeRun(context.withoutSignal) @@ -274,17 +274,28 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ) => flatMap((_) => effect); - /// {@category do_notation} - Effect<V, L, R> mapEnv<V>(Context<E> Function(Context<V> context) f) => + /// {@category context} + Effect<V, L, R> mapContext<V>(Context<E> Function(Context<V> context) f) => Effect.from( (context) => _unsafeRun(f(context)), ); - /// {@category do_notation} - Effect<Null, L, R> provide(E env) => + /// {@category context} + Effect<V, L, R> mapEnv<V>(E Function(V env) f) => Effect.from( + (context) => _unsafeRun( + Context(env: f(context.env), signal: context.signal), + ), + ); + + /// {@category context} + Effect<Null, L, R> provide(Context<E> context) => + Effect.from((_) => _unsafeRun(context)); + + /// {@category context} + Effect<Null, L, R> provideEnv(E env) => Effect.from((_) => _unsafeRun(Context.env(env))); - /// {@category do_notation} + /// {@category context} Effect<V, L, R> provideEffect<V>(Effect<V, L, E> effect) => Effect.from( (context) => effect._unsafeRun(context).then( (exit) => switch (exit) { @@ -294,7 +305,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); - /// {@category do_notation} + /// {@category context} static Effect<E, L, E> env<E, L>() => Effect.from( (context) => Right(context.env), ); @@ -624,7 +635,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { } extension ProvideNull<L, R> on Effect<Null, L, R> { - /// {@category do_notation} + /// {@category context} Effect<V, L, R> withEnv<V>() => Effect.from( (context) => _unsafeRun(Context.env(null)), ); diff --git a/packages/fpdart/test/src/effect/deferred_test.dart b/packages/fpdart/test/src/effect/deferred_test.dart index 9603600..a77bc58 100644 --- a/packages/fpdart/test/src/effect/deferred_test.dart +++ b/packages/fpdart/test/src/effect/deferred_test.dart @@ -14,7 +14,7 @@ void main() { .zipRight(deferred.completeExit(Right(2))), ])); - final value = await $.async(deferred.future()); + final value = await $.async(deferred.wait()); return value; }); diff --git a/packages/fpdart/test/src/effect/effect_do_notation_test.dart b/packages/fpdart/test/src/effect/effect_do_notation_test.dart index bdacf57..28f199d 100644 --- a/packages/fpdart/test/src/effect/effect_do_notation_test.dart +++ b/packages/fpdart/test/src/effect/effect_do_notation_test.dart @@ -12,7 +12,7 @@ void main() { return env.length; }); - final program = main.provide("abc"); + final program = main.provideEnv("abc"); final result = program.runSync(); expect(result, 3); }); @@ -49,12 +49,12 @@ void main() { final subMain = Effect<int, String, int>.from( (context) => Right(context.env + 1)); final main = Effect<String, String, int>.gen(($) { - final value = $.sync( - subMain.mapEnv((context) => Context.env(context.env.length))); + final value = $.sync(subMain + .mapContext((context) => Context.env(context.env.length))); return value; }); - final result = main.provide("abc").runSync(); + final result = main.provideEnv("abc").runSync(); expect(result, 4); }); }); From 82d1ed64fdbd403809fd233fa82015057e429060 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 29 Mar 2024 06:25:17 +0900 Subject: [PATCH 77/91] fpdart 2.0.0-dev.2 --- packages/fpdart/CHANGELOG.md | 9 +++++++++ packages/fpdart/README.md | 3 ++- packages/fpdart/pubspec.yaml | 8 ++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/fpdart/CHANGELOG.md b/packages/fpdart/CHANGELOG.md index faa383c..8d6946e 100644 --- a/packages/fpdart/CHANGELOG.md +++ b/packages/fpdart/CHANGELOG.md @@ -1,3 +1,12 @@ +## v2.0.0-dev.2 - 29 March 2024 +- Complete `Option` and `Either` API +- Execute `Effect` using `provide` (with `Null` as dependency) +- Fixed implementation of running `Effect` and catching `Cause` +- Added interruption (`Cause.Interrupted`) + - `Deferred` + - `Context` + - New methods (`raceAll`, `race`, `delay`, `sleep`, `timeout`) + ## v2.0.0-dev.1 - 23 March 2024 - Initial preview release of `fpdart` v2 - Refactoring to use `Effect` class diff --git a/packages/fpdart/README.md b/packages/fpdart/README.md index ae3ca5b..cbdd787 100644 --- a/packages/fpdart/README.md +++ b/packages/fpdart/README.md @@ -123,7 +123,7 @@ Interested in what `fpdart` is and how it came to be? ## 💻 Installation ```shell -dart pub add fpdart:'^2.0.0' +dart pub add fpdart:'v2.0.0-dev.2' ``` ## ✨ Examples @@ -499,6 +499,7 @@ In general, **any contribution or feedback is welcome** (and encouraged!). ## 📃 Versioning +- v2.0.0-dev.2 - 29 March 2024 - v2.0.0-dev.1 - 23 March 2024 *** diff --git a/packages/fpdart/pubspec.yaml b/packages/fpdart/pubspec.yaml index b4a2a6d..773644e 100644 --- a/packages/fpdart/pubspec.yaml +++ b/packages/fpdart/pubspec.yaml @@ -1,8 +1,8 @@ name: fpdart description: > - Functional programming in Dart and Flutter. - All the main functional programming types and patterns fully documented, tested, and with examples. -version: 2.0.0-dev.1 + Functional Effect System in Dart and Flutter. + Build composable, type safe, maintainable and testable apps with an extensive API fully tested and documented. +version: 2.0.0-dev.2 homepage: https://www.sandromaglione.com/ repository: https://github.com/SandroMaglione/fpdart author: Maglione Sandro <lass.maglio@gmail.com> @@ -21,5 +21,5 @@ dev_dependencies: collection: ^1.18.0 screenshots: - - description: "Basic usage of fpdart Option, Either, TaskEither types." + - description: "Basic usage of fpdart Effect type." path: example/screenshot_fpdart.png From 56f9ad795e8f2f49a87c83bb3b92aac3dc7d5a10 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Fri, 29 Mar 2024 15:32:31 +0900 Subject: [PATCH 78/91] const `None` --- packages/fpdart/lib/src/deferred.dart | 2 +- packages/fpdart/lib/src/effect.dart | 2 +- packages/fpdart/lib/src/either.dart | 4 +-- .../lib/src/extension/iterable_extension.dart | 12 ++++---- .../lib/src/extension/map_extension.dart | 6 ++-- packages/fpdart/lib/src/option.dart | 29 +++++++++---------- packages/fpdart/test/src/option_test.dart | 8 ++--- 7 files changed, 29 insertions(+), 34 deletions(-) diff --git a/packages/fpdart/lib/src/deferred.dart b/packages/fpdart/lib/src/deferred.dart index c4c5dc9..e111339 100644 --- a/packages/fpdart/lib/src/deferred.dart +++ b/packages/fpdart/lib/src/deferred.dart @@ -1,7 +1,7 @@ part of 'effect.dart'; final class Deferred<L, R> { - Option<Exit<L, R>> _state = None(); + Option<Exit<L, R>> _state = const None(); Completer<Exit<L, R>>? __completer; Deferred._(); diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 2af3324..afced12 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -339,7 +339,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Failure<L>() => Right(None()), + Failure() => Right<Cause<Never>, Option<R>>(const None()), Die() => Left(cause), Interrupted() => Left(cause), }, diff --git a/packages/fpdart/lib/src/either.dart b/packages/fpdart/lib/src/either.dart index c59098e..b54a417 100644 --- a/packages/fpdart/lib/src/either.dart +++ b/packages/fpdart/lib/src/either.dart @@ -143,7 +143,7 @@ final class Right<L, R> extends Either<L, R> { predicate(value) ? Right(value) : Left(orLeftWith(value)); @override - Option<L> getLeft() => None(); + Option<L> getLeft() => const None(); @override Either<L, R> tap<C>(Either<L, C> Function(R r) f) => @@ -188,7 +188,7 @@ final class Left<L, R> extends Either<L, R> { R? getOrNull() => null; @override - Option<R> getRight() => None(); + Option<R> getRight() => const None(); @override bool operator ==(Object other) => (other is Left) && other.value == value; diff --git a/packages/fpdart/lib/src/extension/iterable_extension.dart b/packages/fpdart/lib/src/extension/iterable_extension.dart index 64cc42e..cf215e4 100644 --- a/packages/fpdart/lib/src/extension/iterable_extension.dart +++ b/packages/fpdart/lib/src/extension/iterable_extension.dart @@ -18,7 +18,7 @@ extension FpdartOnIterable<T> on Iterable<T> { Option<T> get head { var it = iterator; if (it.moveNext()) return Some(it.current); - return None(); + return const None(); } /// {@macro fpdart_iterable_extension_head} @@ -32,7 +32,7 @@ extension FpdartOnIterable<T> on Iterable<T> { /// **Note**: Because accessing the last element of an [Iterable] requires /// stepping through all the other elements, `lastOption` **can be slow**. Option<T> get lastOption { - if (isEmpty) return None(); + if (isEmpty) return const None(); return Some(last); } @@ -46,7 +46,7 @@ extension FpdartOnIterable<T> on Iterable<T> { /// at that point, the returned iterable will also be empty, same /// as if this iterable has only one element. Option<Iterable<T>> get tail { - if (isEmpty) return None(); + if (isEmpty) return const None(); return Some(skip(1)); } @@ -60,7 +60,7 @@ extension FpdartOnIterable<T> on Iterable<T> { /// at that point, the returned iterable will also be empty, same /// as if this iterable has only one element. Option<Iterable<T>> get init { - if (isEmpty) return None(); + if (isEmpty) return const None(); return Some(this.dropRight(1)); } @@ -331,7 +331,7 @@ extension FpdartOnIterable<T> on Iterable<T> { } return Some(min); } - return None(); + return const None(); } /// The least element of this [Iterable] based on `order`. @@ -348,7 +348,7 @@ extension FpdartOnIterable<T> on Iterable<T> { } return Some(min); } - return None(); + return const None(); } /// Apply all the functions inside `iterable` to this [Iterable]. diff --git a/packages/fpdart/lib/src/extension/map_extension.dart b/packages/fpdart/lib/src/extension/map_extension.dart index 25b8deb..a5bf0f7 100644 --- a/packages/fpdart/lib/src/extension/map_extension.dart +++ b/packages/fpdart/lib/src/extension/map_extension.dart @@ -55,7 +55,7 @@ extension FpdartOnMap<K, V> on Map<K, V> { var value = this[key]; if (value != null) return Some(value); if (containsKey(key)) return Some(value as V); - return None(); + return const None(); } /// Get the value and key at given `key` if present, otherwise return [None]. @@ -63,7 +63,7 @@ extension FpdartOnMap<K, V> on Map<K, V> { final value = this[key]; if (value != null) return Some((key, value)); if (containsKey(key)) return Some((key, value as V)); - return None(); + return const None(); } /// Return an [Option] that conditionally accesses map keys, only if they match the @@ -78,7 +78,7 @@ extension FpdartOnMap<K, V> on Map<K, V> { Option<T> extract<T>(K key) { final value = this[key]; if (value is T) return Some(value); - return None(); + return const None(); } /// Return an [Option] that conditionally accesses map keys if they contain a value diff --git a/packages/fpdart/lib/src/option.dart b/packages/fpdart/lib/src/option.dart index 5540c2f..9e883dc 100644 --- a/packages/fpdart/lib/src/option.dart +++ b/packages/fpdart/lib/src/option.dart @@ -8,24 +8,24 @@ sealed class Option<R> extends IEffect<Never, Never, R> { static Option<R> safeCastStrict<R, V>(V value) { if (value is R) return Some(value); - return None(); + return const None(); } factory Option.fromPredicate(R value, bool Function(R r) predicate) { if (predicate(value)) return Some(value); - return None(); + return const None(); } factory Option.fromNullable(R? value) { if (value != null) return Some(value); - return None(); + return const None(); } factory Option.tryCatch(R Function() f) { try { return Some(f()); } catch (_) { - return None(); + return const None(); } } @@ -33,7 +33,7 @@ sealed class Option<R> extends IEffect<Never, Never, R> { dynamic json, R Function(dynamic json) fromJson, ) => - json != null ? Option.tryCatch(() => fromJson(json)) : None(); + json != null ? Option.tryCatch(() => fromJson(json)) : const None(); static Iterable<R> getSomes<R>(Iterable<Option<R>> iterable) sync* { for (var option in iterable) { @@ -115,21 +115,18 @@ final class Some<R> extends Option<R> { @override Option<R> filter(bool Function(R r) f) { if (f(value)) return Some(value); - return None(); + return const None(); } @override Option<C> filterMap<C>(Option<C> Function(R r) f) { if (f(value) case Some(value: final value)) return Some(value); - return None(); + return const None(); } } final class None extends Option<Never> { - static const None _none = None._instance(); - const None._instance(); - - factory None() => _none; + const None(); @override Effect<Never, Never, Never> get asEffect => @@ -143,20 +140,20 @@ final class None extends Option<Never> { Null getOrNull() => null; @override - Object? toJson(Object? Function(Never value) toJson) => None(); + Object? toJson(Object? Function(Never value) toJson) => const None(); @override String toString() => 'None'; @override - Option<C> andThen<C>(C Function(Never r) f) => None(); + Option<C> andThen<C>(C Function(Never r) f) => const None(); @override - Option<Never> tap<C>(Option<C> Function(Never r) f) => None(); + Option<Never> tap<C>(Option<C> Function(Never r) f) => const None(); @override - Option<Never> filter(bool Function(Never r) f) => None(); + Option<Never> filter(bool Function(Never r) f) => const None(); @override - Option<C> filterMap<C>(Option<C> Function(Never r) f) => None(); + Option<C> filterMap<C>(Option<C> Function(Never r) f) => const None(); } diff --git a/packages/fpdart/test/src/option_test.dart b/packages/fpdart/test/src/option_test.dart index c2fb63b..3bd8950 100644 --- a/packages/fpdart/test/src/option_test.dart +++ b/packages/fpdart/test/src/option_test.dart @@ -1,16 +1,14 @@ import "package:fpdart/fpdart.dart"; import "package:test/test.dart"; -class CustomError implements Exception {} - void main() { group( "Option", () { group('None', () { - test('singleton', () { - final none1 = None(); - final none2 = None(); + test('const', () { + final none1 = const None(); + final none2 = const None(); expect(none1, none2); }); }); From bed477ad8660cd670e9ddd534f522eb4b6af17c5 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sat, 30 Mar 2024 06:22:30 +0900 Subject: [PATCH 79/91] introduce `Scope` --- packages/fpdart/lib/src/effect.dart | 28 ++++++++++++ .../lib/src/extension/iterable_extension.dart | 4 ++ packages/fpdart/lib/src/scope.dart | 44 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 packages/fpdart/lib/src/scope.dart diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index afced12..cb23690 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -10,6 +10,7 @@ part 'context.dart'; part 'deferred.dart'; part 'either.dart'; part 'option.dart'; +part 'scope.dart'; typedef EffectGen<E, L> = ({ FutureOr<A> Function<A>(IEffect<E, L, A>) async, @@ -160,6 +161,10 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (_) => f().then(Right.new), ); + /// {@category constructors} + factory Effect.lazy(Effect<E, L, R> Function() effect) => + Effect.from((context) => effect().__unsafeRun(context)); + /// {@category constructors} factory Effect.fail(L value) => Effect.from((_) => Left(Failure(value))); @@ -480,6 +485,15 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { Effect<E, L, R> race(Effect<E, L, R> effect) => Effect.raceAll([this, effect]); + /// {@category sequencing} + Effect<E, L, R> alwaysIgnore<C>(Effect<E, L, C> effect) => Effect.from( + (context) => race(context.signal.wait()).__unsafeRun(context).then( + (exit) => effect.__unsafeRun(context.withoutSignal).then( + (_) => exit, + ), + ), + ); + /// {@category sequencing} Effect<E, L, C> flatMap<C>(Effect<E, L, C> Function(R r) f) => Effect.from( (context) => _unsafeRun(context).then( @@ -632,6 +646,20 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category interruption} Effect<E, Never, R> interrupt() => Effect.failCause(const Interrupted()); + + /// {@category scoping} + Effect<Scope<E>, L, R> get scopedEnv => Effect.from( + (context) => __unsafeRun(context.withEnv(context.env.env)), + ); +} + +extension EffectWithScope<E, L, R> on Effect<Scope<E>, L, R> { + Effect<E, L, R> get scoped => Effect.from((context) { + final scope = Scope.withEnv(context.env); + return alwaysIgnore(scope.closeScope()).__unsafeRun( + context.withEnv(scope), + ); + }); } extension ProvideNull<L, R> on Effect<Null, L, R> { diff --git a/packages/fpdart/lib/src/extension/iterable_extension.dart b/packages/fpdart/lib/src/extension/iterable_extension.dart index cf215e4..240ae25 100644 --- a/packages/fpdart/lib/src/extension/iterable_extension.dart +++ b/packages/fpdart/lib/src/extension/iterable_extension.dart @@ -411,6 +411,10 @@ extension FpdartOnIterableOfIterable<T> on Iterable<Iterable<T>> { Iterable<T> get flatten => expand(identity); } +extension IterableEffect<E, L, R> on Iterable<Effect<E, L, R>> { + Effect<E, L, Iterable<R>> get all => Effect.all(this); +} + extension FpdartSequenceIterableOption<R> on Iterable<Option<R>> { Iterable<R> get getSomes => Option.getSomes(this); } diff --git a/packages/fpdart/lib/src/scope.dart b/packages/fpdart/lib/src/scope.dart new file mode 100644 index 0000000..bbf637c --- /dev/null +++ b/packages/fpdart/lib/src/scope.dart @@ -0,0 +1,44 @@ +part of "effect.dart"; + +mixin ScopeMixin { + bool get scopeClosable => false; + + final scopeFinalizers = <Effect<Null, Never, Unit>>[]; + + Effect<E, L, Unit> addScopeFinalizer<E, L>( + Effect<Null, Never, Unit> finalizer, + ) => + Effect.functionSucceed(() { + scopeFinalizers.add(finalizer); + return unit; + }); + + Effect<E, L, Unit> removeScopeFinalizer<L, E>( + Effect<Null, Never, Unit> finalizer, + ) => + Effect.functionSucceed(() { + scopeFinalizers.remove(finalizer); + return unit; + }); + + Effect<E, L, Unit> closeScope<E, L>() => Effect.lazy( + () => scopeFinalizers.reversed.all.zipRight(Effect.functionSucceed( + () { + scopeFinalizers.clear(); + return unit; + }, + )).withEnv(), + ); +} + +class Scope<R> with ScopeMixin { + final bool _closable; + final R env; + Scope._(this.env, this._closable); + + factory Scope.withEnv(R env, [bool closable = false]) => + Scope._(env, closable); + + @override + bool get scopeClosable => _closable; +} From 3ca43b8ce52253f9c5c4bca2e2c22da7e512fdf8 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 4 Apr 2024 06:22:11 +0900 Subject: [PATCH 80/91] `Scope` with test --- packages/fpdart/lib/src/effect.dart | 73 +++++++++++++++++-- .../test/src/effect/effect_scoping_test.dart | 57 +++++++++++++++ 2 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 packages/fpdart/test/src/effect/effect_scoping_test.dart diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index cb23690..f30ba3b 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -293,12 +293,21 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category context} - Effect<Null, L, R> provide(Context<E> context) => - Effect.from((_) => _unsafeRun(context)); + Effect<Null, L, R> provide(Context<E> context) { + final env = context.env; + final effect = env is ScopeMixin && !env.scopeClosable + ? alwaysIgnore(env.closeScope()) + : this; + return Effect.from((_) => effect._unsafeRun(context)); + } /// {@category context} - Effect<Null, L, R> provideEnv(E env) => - Effect.from((_) => _unsafeRun(Context.env(env))); + Effect<Null, L, R> provideEnv(E env) { + final effect = env is ScopeMixin && !env.scopeClosable + ? alwaysIgnore(env.closeScope()) + : this; + return Effect.from((_) => effect._unsafeRun(Context.env(env))); + } /// {@category context} Effect<V, L, R> provideEffect<V>(Effect<V, L, E> effect) => Effect.from( @@ -504,10 +513,30 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); + /// {@category sequencing} + Effect<E, L, C> flatMapEnv<C>(Effect<E, L, C> Function(R r, E env) f) => + Effect.from( + (context) => _unsafeRun(context).then( + (exit) => switch (exit) { + Left(value: final cause) => Left(cause), + Right(value: final value) => + f(value, context.env)._unsafeRun(context), + }, + ), + ); + /// {@category sequencing} Effect<E, L, R> tap<C>(Effect<E, L, C> Function(R r) f) => flatMap((r) => f(r).map((_) => r)); + /// {@category sequencing} + Effect<E, L, R> tapEnv<C>( + Effect<E, L, C> Function(R r, E env) f, + ) => + flatMapEnv( + (r, env) => f(r, env).map((_) => r), + ); + /// {@category sequencing} Effect<E, L, R> tapError<C>(Effect<E, C, R> Function(L l) f) => Effect.from( (context) => _unsafeRun(context).then( @@ -646,15 +675,45 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category interruption} Effect<E, Never, R> interrupt() => Effect.failCause(const Interrupted()); +} + +extension EffectWithScopeFinalizer<E extends ScopeMixin, L, R> + on Effect<E, L, R> { + /// {@category scoping} + Effect<E, L, R> addFinalizer(Effect<Null, Never, Unit> release) => + tapEnv((_, env) => env.addScopeFinalizer(release)); + + /// {@category scoping} + Effect<E, L, R> acquireRelease( + Effect<Null, Never, Unit> Function(R r) release, + ) => + tap((r) => addFinalizer(release(r))); +} +extension EffectNoScopeFinalizer<E, L, R> on Effect<E, L, R> { /// {@category scoping} - Effect<Scope<E>, L, R> get scopedEnv => Effect.from( - (context) => __unsafeRun(context.withEnv(context.env.env)), + Effect<Scope<E>, L, R> get withScope => Effect<Scope<E>, L, R>.from( + (ctx) => _unsafeRun(ctx.withEnv(ctx.env.env)), + ); + + /// {@category scoping} + Effect<Scope<E>, L, R> addFinalizer(Effect<Null, Never, Unit> release) => + withScope.tapEnv( + (_, env) => env.addScopeFinalizer(release), + ); + + /// {@category scoping} + Effect<Scope<E>, L, R> acquireRelease( + Effect<Null, Never, Unit> Function(R r) release, + ) => + withScope.tapEnv( + (r, _) => _.addScopeFinalizer(release(r)), ); } extension EffectWithScope<E, L, R> on Effect<Scope<E>, L, R> { - Effect<E, L, R> get scoped => Effect.from((context) { + /// {@category scoping} + Effect<E, L, R> get provideScope => Effect.from((context) { final scope = Scope.withEnv(context.env); return alwaysIgnore(scope.closeScope()).__unsafeRun( context.withEnv(scope), diff --git a/packages/fpdart/test/src/effect/effect_scoping_test.dart b/packages/fpdart/test/src/effect/effect_scoping_test.dart new file mode 100644 index 0000000..326b50f --- /dev/null +++ b/packages/fpdart/test/src/effect/effect_scoping_test.dart @@ -0,0 +1,57 @@ +import "package:fpdart/fpdart.dart"; +import "package:test/test.dart"; + +class CustomError implements Exception {} + +void main() { + group( + "Effect scoping", + () { + test('add and release Scope finalizer', () async { + var mutable = 0; + final main = + Effect<Null, String, int>.succeed(10).withScope.addFinalizer( + Effect.functionSucceed(() { + mutable += 1; + return unit; + }), + ).provideScope; + + await main.runFuture(); + expect(mutable, 1); + }); + + group('acquireRelease', () { + test('release when successful', () async { + var mutable = 0; + final main = Effect<Null, String, int>.succeed(10) + .acquireRelease( + (r) => Effect.functionSucceed(() { + mutable = r; + return unit; + }), + ) + .provideScope; + + await main.runFuture(); + expect(mutable, 10); + }); + + test('no release when failed', () async { + var mutable = 0; + final main = Effect<Null, String, int>.fail("error") + .acquireRelease( + (r) => Effect.functionSucceed(() { + mutable = r; + return unit; + }), + ) + .provideScope; + + await main.flip().runFuture(); + expect(mutable, 0); + }); + }); + }, + ); +} From 26180a7ce4697d97742e37747c38d09c1d5f03a9 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 4 Apr 2024 06:28:13 +0900 Subject: [PATCH 81/91] test closable `Scope` --- packages/fpdart/lib/src/scope.dart | 2 +- .../test/src/effect/effect_scoping_test.dart | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/fpdart/lib/src/scope.dart b/packages/fpdart/lib/src/scope.dart index bbf637c..2905396 100644 --- a/packages/fpdart/lib/src/scope.dart +++ b/packages/fpdart/lib/src/scope.dart @@ -36,7 +36,7 @@ class Scope<R> with ScopeMixin { final R env; Scope._(this.env, this._closable); - factory Scope.withEnv(R env, [bool closable = false]) => + factory Scope.withEnv(R env, {bool closable = false}) => Scope._(env, closable); @override diff --git a/packages/fpdart/test/src/effect/effect_scoping_test.dart b/packages/fpdart/test/src/effect/effect_scoping_test.dart index 326b50f..0a15055 100644 --- a/packages/fpdart/test/src/effect/effect_scoping_test.dart +++ b/packages/fpdart/test/src/effect/effect_scoping_test.dart @@ -21,6 +21,23 @@ void main() { expect(mutable, 1); }); + test('closable Scope', () async { + final scope = Scope.withEnv(true, closable: true); + var mutable = 0; + final main = + Effect<bool, String, int>.succeed(10).withScope.addFinalizer( + Effect.functionSucceed(() { + mutable += 1; + return unit; + }), + ); + + await main.provide(Context.env(scope)).runFuture(); + expect(mutable, 0); + scope.closeScope<Null, Never>().runSync(); + expect(mutable, 1); + }); + group('acquireRelease', () { test('release when successful', () async { var mutable = 0; From 895f80ad1677a3c32f25d990f98e472bb7350ba4 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 4 Apr 2024 06:32:44 +0900 Subject: [PATCH 82/91] v2.0.0-dev.3 --- packages/fpdart/CHANGELOG.md | 4 ++++ packages/fpdart/README.md | 2 +- packages/fpdart/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/fpdart/CHANGELOG.md b/packages/fpdart/CHANGELOG.md index 8d6946e..0d975ce 100644 --- a/packages/fpdart/CHANGELOG.md +++ b/packages/fpdart/CHANGELOG.md @@ -1,3 +1,7 @@ +## v2.0.0-dev.3 - 4 April 2024 +- Added `Scope` +- `const` constructor for `None` + ## v2.0.0-dev.2 - 29 March 2024 - Complete `Option` and `Either` API - Execute `Effect` using `provide` (with `Null` as dependency) diff --git a/packages/fpdart/README.md b/packages/fpdart/README.md index cbdd787..17cbcd0 100644 --- a/packages/fpdart/README.md +++ b/packages/fpdart/README.md @@ -123,7 +123,7 @@ Interested in what `fpdart` is and how it came to be? ## 💻 Installation ```shell -dart pub add fpdart:'v2.0.0-dev.2' +dart pub add fpdart:'v2.0.0-dev.3' ``` ## ✨ Examples diff --git a/packages/fpdart/pubspec.yaml b/packages/fpdart/pubspec.yaml index 773644e..6b7e072 100644 --- a/packages/fpdart/pubspec.yaml +++ b/packages/fpdart/pubspec.yaml @@ -2,7 +2,7 @@ name: fpdart description: > Functional Effect System in Dart and Flutter. Build composable, type safe, maintainable and testable apps with an extensive API fully tested and documented. -version: 2.0.0-dev.2 +version: 2.0.0-dev.3 homepage: https://www.sandromaglione.com/ repository: https://github.com/SandroMaglione/fpdart author: Maglione Sandro <lass.maglio@gmail.com> From 8e34681c6beae8cc474eabb549a5420c91c356da Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 4 Apr 2024 15:27:02 +0900 Subject: [PATCH 83/91] refactor and remove `__unsafeRun` --- packages/fpdart/lib/src/effect.dart | 44 ++++++++++++----------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index f30ba3b..33a9af3 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -74,29 +74,21 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { return "Effect(${_unsafeRun.runtimeType})"; } - FutureOr<Exit<L, R>> __unsafeRun(Context<E> context) { - if (context.signal.unsafeCompleted) { - return const Left(Interrupted()); - } - - try { - return _unsafeRun(context).then((exit) { - if (context.signal.unsafeCompleted) { - return const Left(Interrupted()); - } - - return exit; - }); - } catch (err, stackTrace) { - return Left(Die(err, stackTrace)); - } - } - /// {@category constructors} factory Effect.from(FutureOr<Exit<L, R>> Function(Context<E> context) run) => Effect._((context) { try { - return run(context); + if (context.signal.unsafeCompleted) { + return const Left(Interrupted()); + } + + return run(context).then((exit) { + if (context.signal.unsafeCompleted) { + return const Left(Interrupted()); + } + + return exit; + }); } on Cause<L> catch (cause) { return Left(cause); } catch (error, stackTrace) { @@ -163,7 +155,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category constructors} factory Effect.lazy(Effect<E, L, R> Function() effect) => - Effect.from((context) => effect().__unsafeRun(context)); + Effect.from((context) => effect()._unsafeRun(context)); /// {@category constructors} factory Effect.fail(L value) => Effect.from((_) => Left(Failure(value))); @@ -182,7 +174,7 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { for (final effect in iterable) { effect - .__unsafeRun(context.withSignal(signal)) + ._unsafeRun(context.withSignal(signal)) .then(deferred.unsafeCompleteExit); if (deferred.unsafeCompleted) { @@ -190,10 +182,10 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { } } - return deferred.wait<E>().__unsafeRun(context).then( + return deferred.wait<E>()._unsafeRun(context).then( (exit) => signal .failCause<E, L>(const Interrupted()) - .__unsafeRun(context.withoutSignal) + ._unsafeRun(context.withoutSignal) .then((_) => exit), ); }); @@ -496,8 +488,8 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { /// {@category sequencing} Effect<E, L, R> alwaysIgnore<C>(Effect<E, L, C> effect) => Effect.from( - (context) => race(context.signal.wait()).__unsafeRun(context).then( - (exit) => effect.__unsafeRun(context.withoutSignal).then( + (context) => race(context.signal.wait())._unsafeRun(context).then( + (exit) => effect._unsafeRun(context.withoutSignal).then( (_) => exit, ), ), @@ -715,7 +707,7 @@ extension EffectWithScope<E, L, R> on Effect<Scope<E>, L, R> { /// {@category scoping} Effect<E, L, R> get provideScope => Effect.from((context) { final scope = Scope.withEnv(context.env); - return alwaysIgnore(scope.closeScope()).__unsafeRun( + return alwaysIgnore(scope.closeScope())._unsafeRun( context.withEnv(scope), ); }); From 43f059200dcd430d9e5f621213c21609837caaba Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 4 Apr 2024 15:27:38 +0900 Subject: [PATCH 84/91] removed `tryCatch` `onCancel` --- packages/fpdart/lib/src/effect.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 33a9af3..7c1abc4 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -120,7 +120,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { factory Effect.tryCatch({ required FutureOr<R> Function() execute, required L Function(Object error, StackTrace stackTrace) onError, - FutureOr<dynamic> Function()? onCancel, }) => Effect.from( (env) { From 2f802400ac49b9a0ec0d5ebdc4024b0fef731d7a Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 4 Apr 2024 15:28:57 +0900 Subject: [PATCH 85/91] renamed `succeedLazy` and `failLazy` --- examples/fpdart_http/lib/main.dart | 2 +- examples/poke_api/lib/main.dart | 2 +- packages/fpdart/lib/src/deferred.dart | 2 +- packages/fpdart/lib/src/effect.dart | 4 ++-- packages/fpdart/lib/src/scope.dart | 6 +++--- .../fpdart/test/src/effect/effect_collecting_test.dart | 2 +- .../fpdart/test/src/effect/effect_constructors_test.dart | 4 ++-- .../test/src/effect/effect_error_handling_test.dart | 2 +- packages/fpdart/test/src/effect/effect_scoping_test.dart | 8 ++++---- .../fpdart/test/src/effect/effect_sequencing_test.dart | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/fpdart_http/lib/main.dart b/examples/fpdart_http/lib/main.dart index 9257f0a..320c721 100644 --- a/examples/fpdart_http/lib/main.dart +++ b/examples/fpdart_http/lib/main.dart @@ -9,7 +9,7 @@ void main() async { ) .timeout(Duration(milliseconds: 1300)) .tap( - (response) => Effect.functionSucceed( + (response) => Effect.succeedLazy( () => print(response.body), ), ) diff --git a/examples/poke_api/lib/main.dart b/examples/poke_api/lib/main.dart index 090e748..a8d513c 100644 --- a/examples/poke_api/lib/main.dart +++ b/examples/poke_api/lib/main.dart @@ -68,7 +68,7 @@ void main() async { final exit = await program("9722") .map((pokemon) => print(pokemon)) .catchError<void>( - (error) => Effect.functionSucceed( + (error) => Effect.succeedLazy( () => print("No pokemon: $error"), ), ) diff --git a/packages/fpdart/lib/src/deferred.dart b/packages/fpdart/lib/src/deferred.dart index e111339..a1af7cd 100644 --- a/packages/fpdart/lib/src/deferred.dart +++ b/packages/fpdart/lib/src/deferred.dart @@ -34,7 +34,7 @@ final class Deferred<L, R> { } Effect<E, C, Unit> completeExit<E, C>(Exit<L, R> exit) => - Effect.functionSucceed(() { + Effect.succeedLazy(() { switch (_state) { case None(): unsafeCompleteExit(exit); diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 7c1abc4..b834a04 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -143,12 +143,12 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); /// {@category constructors} - factory Effect.functionFail(FutureOr<Cause<L>> Function() f) => Effect.from( + factory Effect.failLazy(FutureOr<Cause<L>> Function() f) => Effect.from( (_) => f().then(Left.new), ); /// {@category constructors} - factory Effect.functionSucceed(FutureOr<R> Function() f) => Effect.from( + factory Effect.succeedLazy(FutureOr<R> Function() f) => Effect.from( (_) => f().then(Right.new), ); diff --git a/packages/fpdart/lib/src/scope.dart b/packages/fpdart/lib/src/scope.dart index 2905396..9b30ad8 100644 --- a/packages/fpdart/lib/src/scope.dart +++ b/packages/fpdart/lib/src/scope.dart @@ -8,7 +8,7 @@ mixin ScopeMixin { Effect<E, L, Unit> addScopeFinalizer<E, L>( Effect<Null, Never, Unit> finalizer, ) => - Effect.functionSucceed(() { + Effect.succeedLazy(() { scopeFinalizers.add(finalizer); return unit; }); @@ -16,13 +16,13 @@ mixin ScopeMixin { Effect<E, L, Unit> removeScopeFinalizer<L, E>( Effect<Null, Never, Unit> finalizer, ) => - Effect.functionSucceed(() { + Effect.succeedLazy(() { scopeFinalizers.remove(finalizer); return unit; }); Effect<E, L, Unit> closeScope<E, L>() => Effect.lazy( - () => scopeFinalizers.reversed.all.zipRight(Effect.functionSucceed( + () => scopeFinalizers.reversed.all.zipRight(Effect.succeedLazy( () { scopeFinalizers.clear(); return unit; diff --git a/packages/fpdart/test/src/effect/effect_collecting_test.dart b/packages/fpdart/test/src/effect/effect_collecting_test.dart index 84db780..dce3a81 100644 --- a/packages/fpdart/test/src/effect/effect_collecting_test.dart +++ b/packages/fpdart/test/src/effect/effect_collecting_test.dart @@ -20,7 +20,7 @@ void main() { final main = Effect.all<Null, String, int>([ Effect.succeed(10), Effect.fail("10"), - Effect.functionSucceed(() => mutable += 1), + Effect.succeedLazy(() => mutable += 1), Effect.fail("0"), ]); final result = main.flip().runSync(); diff --git a/packages/fpdart/test/src/effect/effect_constructors_test.dart b/packages/fpdart/test/src/effect/effect_constructors_test.dart index 0df5ab4..81e6f67 100644 --- a/packages/fpdart/test/src/effect/effect_constructors_test.dart +++ b/packages/fpdart/test/src/effect/effect_constructors_test.dart @@ -73,7 +73,7 @@ void main() { test('async succeed', () async { final main = Effect<Null, Never, int>.gen(($) async { final value = - await $.async(Effect.functionSucceed(() => Future.value(10))); + await $.async(Effect.succeedLazy(() => Future.value(10))); return value; }); final result = await main.runFuture(); @@ -82,7 +82,7 @@ void main() { test('fail when running async as sync', () async { final main = Effect<Null, Never, int>.gen(($) { - final value = $.sync(Effect.functionSucceed( + final value = $.sync(Effect.succeedLazy( () async => Future.value(10), )); return value; diff --git a/packages/fpdart/test/src/effect/effect_error_handling_test.dart b/packages/fpdart/test/src/effect/effect_error_handling_test.dart index 86ca8de..921fc7c 100644 --- a/packages/fpdart/test/src/effect/effect_error_handling_test.dart +++ b/packages/fpdart/test/src/effect/effect_error_handling_test.dart @@ -7,7 +7,7 @@ void main() { () { group('catchCause', () { test('recover from throw', () { - final result = Effect<Null, Never, String>.functionSucceed(() { + final result = Effect<Null, Never, String>.succeedLazy(() { throw "fail"; }) .catchCause( diff --git a/packages/fpdart/test/src/effect/effect_scoping_test.dart b/packages/fpdart/test/src/effect/effect_scoping_test.dart index 0a15055..63331b1 100644 --- a/packages/fpdart/test/src/effect/effect_scoping_test.dart +++ b/packages/fpdart/test/src/effect/effect_scoping_test.dart @@ -11,7 +11,7 @@ void main() { var mutable = 0; final main = Effect<Null, String, int>.succeed(10).withScope.addFinalizer( - Effect.functionSucceed(() { + Effect.succeedLazy(() { mutable += 1; return unit; }), @@ -26,7 +26,7 @@ void main() { var mutable = 0; final main = Effect<bool, String, int>.succeed(10).withScope.addFinalizer( - Effect.functionSucceed(() { + Effect.succeedLazy(() { mutable += 1; return unit; }), @@ -43,7 +43,7 @@ void main() { var mutable = 0; final main = Effect<Null, String, int>.succeed(10) .acquireRelease( - (r) => Effect.functionSucceed(() { + (r) => Effect.succeedLazy(() { mutable = r; return unit; }), @@ -58,7 +58,7 @@ void main() { var mutable = 0; final main = Effect<Null, String, int>.fail("error") .acquireRelease( - (r) => Effect.functionSucceed(() { + (r) => Effect.succeedLazy(() { mutable = r; return unit; }), diff --git a/packages/fpdart/test/src/effect/effect_sequencing_test.dart b/packages/fpdart/test/src/effect/effect_sequencing_test.dart index 8d92543..48a5598 100644 --- a/packages/fpdart/test/src/effect/effect_sequencing_test.dart +++ b/packages/fpdart/test/src/effect/effect_sequencing_test.dart @@ -22,7 +22,7 @@ void main() { test('tap', () { var mutable = 0; final main = Effect<Null, String, int>.succeed(10).tap( - (_) => Effect.functionSucceed(() { + (_) => Effect.succeedLazy(() { mutable += 1; }), ); From f691c1ea85071974f657e6415c4821738b685b59 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 4 Apr 2024 17:10:47 +0900 Subject: [PATCH 86/91] scope methods in `Effect` --- packages/fpdart/lib/src/effect.dart | 65 +++++++++------ .../test/src/{effect => }/deferred_test.dart | 0 .../test/src/effect/effect_context_test.dart | 83 +++++++++++++++++++ .../src/effect/effect_interruption_test.dart | 12 +-- packages/fpdart/test/src/test_extension.dart | 22 +++++ 5 files changed, 150 insertions(+), 32 deletions(-) rename packages/fpdart/test/src/{effect => }/deferred_test.dart (100%) create mode 100644 packages/fpdart/test/src/effect/effect_context_test.dart create mode 100644 packages/fpdart/test/src/test_extension.dart diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index b834a04..f9d6bee 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -241,7 +241,10 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { static Effect<E, L, Iterable<R>> all<E, L, R>( Iterable<Effect<E, L, R>> iterable, ) => - Effect.forEach(iterable, (a, _) => a); + Effect.forEach( + iterable, + (effect, _) => effect, + ); /// {@category zipping} Effect<E, L, C> zipWith<B, C>( @@ -283,22 +286,33 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ), ); + Effect<E, L, R> _provideEnvCloseScope(E env) => + env is ScopeMixin && !env.scopeClosable + ? Effect<E, L, R>.from( + (context) => _unsafeRun(context).then( + (exit) => switch (exit) { + Left(value: final value) => Left(value), + Right(value: final value) => + env.closeScope<E, L>()._unsafeRun(context).then( + (exit) => switch (exit) { + Left(value: final value) => Left(value), + Right() => Right(value), + }, + ), + }, + ), + ) + : this; + /// {@category context} - Effect<Null, L, R> provide(Context<E> context) { - final env = context.env; - final effect = env is ScopeMixin && !env.scopeClosable - ? alwaysIgnore(env.closeScope()) - : this; - return Effect.from((_) => effect._unsafeRun(context)); - } + Effect<Null, L, R> provide(Context<E> context) => Effect.from( + (_) => _provideEnvCloseScope(context.env)._unsafeRun(context), + ); /// {@category context} - Effect<Null, L, R> provideEnv(E env) { - final effect = env is ScopeMixin && !env.scopeClosable - ? alwaysIgnore(env.closeScope()) - : this; - return Effect.from((_) => effect._unsafeRun(Context.env(env))); - } + Effect<Null, L, R> provideEnv(E env) => Effect.from( + (_) => _provideEnvCloseScope(env)._unsafeRun(Context.env(env)), + ); /// {@category context} Effect<V, L, R> provideEffect<V>(Effect<V, L, E> effect) => Effect.from( @@ -682,14 +696,11 @@ extension EffectWithScopeFinalizer<E extends ScopeMixin, L, R> } extension EffectNoScopeFinalizer<E, L, R> on Effect<E, L, R> { - /// {@category scoping} - Effect<Scope<E>, L, R> get withScope => Effect<Scope<E>, L, R>.from( - (ctx) => _unsafeRun(ctx.withEnv(ctx.env.env)), - ); - /// {@category scoping} Effect<Scope<E>, L, R> addFinalizer(Effect<Null, Never, Unit> release) => - withScope.tapEnv( + Effect<Scope<E>, L, R>.from( + (context) => _unsafeRun(context.withEnv(context.env.env)), + ).tapEnv( (_, env) => env.addScopeFinalizer(release), ); @@ -697,18 +708,20 @@ extension EffectNoScopeFinalizer<E, L, R> on Effect<E, L, R> { Effect<Scope<E>, L, R> acquireRelease( Effect<Null, Never, Unit> Function(R r) release, ) => - withScope.tapEnv( - (r, _) => _.addScopeFinalizer(release(r)), + Effect<Scope<E>, L, R>.from( + (context) => _unsafeRun(context.withEnv(context.env.env)), + ).tapEnv( + (r, env) => env.addScopeFinalizer( + release(r), + ), ); } extension EffectWithScope<E, L, R> on Effect<Scope<E>, L, R> { - /// {@category scoping} + /// {@category context} Effect<E, L, R> get provideScope => Effect.from((context) { final scope = Scope.withEnv(context.env); - return alwaysIgnore(scope.closeScope())._unsafeRun( - context.withEnv(scope), - ); + return _provideEnvCloseScope(scope)._unsafeRun(context.withEnv(scope)); }); } diff --git a/packages/fpdart/test/src/effect/deferred_test.dart b/packages/fpdart/test/src/deferred_test.dart similarity index 100% rename from packages/fpdart/test/src/effect/deferred_test.dart rename to packages/fpdart/test/src/deferred_test.dart diff --git a/packages/fpdart/test/src/effect/effect_context_test.dart b/packages/fpdart/test/src/effect/effect_context_test.dart new file mode 100644 index 0000000..4d37516 --- /dev/null +++ b/packages/fpdart/test/src/effect/effect_context_test.dart @@ -0,0 +1,83 @@ +import "package:fpdart/fpdart.dart"; +import "package:test/test.dart"; + +import "../test_extension.dart"; + +class CustomError implements Exception { + const CustomError(); +} + +void main() { + group( + "Effect context", + () { + group('provideEnv', () { + test('handle throw in closing scope', () async { + final main = Effect<Null, String, int>.succeed(10) + .addFinalizer(Effect.succeedLazy( + () => throw const CustomError(), + )) + .provideEnv(Scope.withEnv(null)); + + final result = await main.runFutureExit(); + + result.expectLeft((value) { + expect(value, isA<Die>()); + if (value is Die) { + expect(value.error, isA<CustomError>()); + } + }); + }); + + test('handle failure in closing scope', () async { + final main = Effect<Null, String, int>.succeed(10) + .addFinalizer(Effect.die(const CustomError())) + .provideEnv(Scope.withEnv(null)); + + final result = await main.runFutureExit(); + + result.expectLeft((value) { + expect(value, isA<Die>()); + if (value is Die) { + expect(value.error, isA<CustomError>()); + } + }); + }); + }); + + group('provideScope', () { + test('handle throw in closing scope', () async { + final main = Effect<Null, String, int>.succeed(10) + .addFinalizer(Effect.succeedLazy( + () => throw const CustomError(), + )) + .provideScope; + + final result = await main.runFutureExit(); + + result.expectLeft((value) { + expect(value, isA<Die>()); + if (value is Die) { + expect(value.error, isA<CustomError>()); + } + }); + }); + + test('handle failure in closing scope', () async { + final main = Effect<Null, String, int>.succeed(10) + .addFinalizer(Effect.die(const CustomError())) + .provideScope; + + final result = await main.runFutureExit(); + + result.expectLeft((value) { + expect(value, isA<Die>()); + if (value is Die) { + expect(value.error, isA<CustomError>()); + } + }); + }); + }); + }, + ); +} diff --git a/packages/fpdart/test/src/effect/effect_interruption_test.dart b/packages/fpdart/test/src/effect/effect_interruption_test.dart index 7855a3f..820721d 100644 --- a/packages/fpdart/test/src/effect/effect_interruption_test.dart +++ b/packages/fpdart/test/src/effect/effect_interruption_test.dart @@ -1,6 +1,8 @@ import "package:fpdart/fpdart.dart"; import "package:test/test.dart"; +import "../test_extension.dart"; + void main() { group( "Effect interruption", @@ -12,12 +14,10 @@ void main() { ); final result = main.runSyncExit(); - switch (result) { - case Right(): - fail("Either expected to be Left: $result"); - case Left(value: final value): - expect(value, isA<Interrupted>()); - } + + result.expectLeft((value) { + expect(value, isA<Interrupted>()); + }); }); }); }, diff --git a/packages/fpdart/test/src/test_extension.dart b/packages/fpdart/test/src/test_extension.dart new file mode 100644 index 0000000..8f9b6f2 --- /dev/null +++ b/packages/fpdart/test/src/test_extension.dart @@ -0,0 +1,22 @@ +import 'package:fpdart/fpdart.dart'; +import 'package:test/test.dart'; + +extension EitherTest<L, R> on Either<L, R> { + void expectLeft(Function(L value) onLeft) { + switch (this) { + case Right(value: final value): + fail("Either expected to be Left, Right instead: $value"); + case Left(value: final value): + onLeft(value); + } + } + + void expectRight(Function(R value) onRight) { + switch (this) { + case Right(value: final value): + onRight(value); + case Left(value: final value): + fail("Either expected to be Right, Left instead: $value"); + } + } +} From 5efd738f88f6f7cfbb501fad29e44de1bcb5f9f0 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Thu, 4 Apr 2024 17:12:01 +0900 Subject: [PATCH 87/91] fix `withScope` issue --- packages/fpdart/test/src/effect/effect_scoping_test.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/fpdart/test/src/effect/effect_scoping_test.dart b/packages/fpdart/test/src/effect/effect_scoping_test.dart index 63331b1..54148e9 100644 --- a/packages/fpdart/test/src/effect/effect_scoping_test.dart +++ b/packages/fpdart/test/src/effect/effect_scoping_test.dart @@ -9,8 +9,7 @@ void main() { () { test('add and release Scope finalizer', () async { var mutable = 0; - final main = - Effect<Null, String, int>.succeed(10).withScope.addFinalizer( + final main = Effect<Null, String, int>.succeed(10).addFinalizer( Effect.succeedLazy(() { mutable += 1; return unit; @@ -24,8 +23,7 @@ void main() { test('closable Scope', () async { final scope = Scope.withEnv(true, closable: true); var mutable = 0; - final main = - Effect<bool, String, int>.succeed(10).withScope.addFinalizer( + final main = Effect<bool, String, int>.succeed(10).addFinalizer( Effect.succeedLazy(() { mutable += 1; return unit; From 1118bd66039da1ac926b178ab347be92117b5df8 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sat, 6 Apr 2024 10:36:34 +0900 Subject: [PATCH 88/91] `async`, `asyncInterrupt`, `sleep` --- packages/fpdart/lib/src/async_context.dart | 12 ++++ packages/fpdart/lib/src/effect.dart | 67 ++++++++++++++++--- .../src/effect/effect_constructors_test.dart | 38 +++++++++++ 3 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 packages/fpdart/lib/src/async_context.dart diff --git a/packages/fpdart/lib/src/async_context.dart b/packages/fpdart/lib/src/async_context.dart new file mode 100644 index 0000000..dd306ca --- /dev/null +++ b/packages/fpdart/lib/src/async_context.dart @@ -0,0 +1,12 @@ +part of "effect.dart"; + +class AsyncContext<L, R> { + final _deferred = Deferred<L, R>.unsafeMake(); + + void succeed(R value) => _deferred.unsafeCompleteExit(Right(value)); + void fail(L error) => _deferred.unsafeCompleteExit(Left(Failure(error))); + void failCause(Cause<L> cause) => _deferred.unsafeCompleteExit(Left(cause)); + void die(dynamic defect) => _deferred.unsafeCompleteExit(Left( + Die(defect, StackTrace.current), + )); +} diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index f9d6bee..d449989 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -6,6 +6,7 @@ import './extension/future_or_extension.dart'; import './extension/iterable_extension.dart'; import 'unit.dart' as fpdart_unit; +part 'async_context.dart'; part 'context.dart'; part 'deferred.dart'; part 'either.dart'; @@ -152,18 +153,70 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { (_) => f().then(Right.new), ); + /// {@category constructors} + factory Effect.fail(L value) => Effect.from((_) => Left(Failure(value))); + + /// {@category constructors} + factory Effect.failCause(Cause<L> cause) => Effect.from((_) => Left(cause)); + + /// {@category constructors} + factory Effect.succeed(R value) => Effect.from((_) => Right(value)); + /// {@category constructors} factory Effect.lazy(Effect<E, L, R> Function() effect) => Effect.from((context) => effect()._unsafeRun(context)); /// {@category constructors} - factory Effect.fail(L value) => Effect.from((_) => Left(Failure(value))); + factory Effect.async(void Function(AsyncContext<L, R> resume) callback) => + Effect.from( + (context) { + final asyncContext = AsyncContext<L, R>(); + callback(asyncContext); + return asyncContext._deferred.wait<E>()._unsafeRun(context); + }, + ); /// {@category constructors} - factory Effect.failCause(Cause<L> cause) => Effect.from((_) => Left(cause)); + factory Effect.asyncInterrupt( + Effect<Null, Never, Unit> Function(AsyncContext<L, R> resume) callback, + ) => + Effect.from((context) { + final asyncContext = AsyncContext<L, R>(); + + final finalizer = callback(asyncContext); + if (asyncContext._deferred.unsafeCompleted) { + return asyncContext._deferred.wait<E>()._unsafeRun(context); + } + + final interruption = context.signal.wait<E>().alwaysIgnore( + finalizer.withEnv<E>(), + ); + + return asyncContext._deferred + .wait<E>() + .race(interruption) + ._unsafeRun(context.withoutSignal); + }); /// {@category constructors} - factory Effect.succeed(R value) => Effect.from((_) => Right(value)); + static Effect<E, L, void> sleep<E, L>(Duration duration) => + Effect.asyncInterrupt( + (resume) { + final timer = Timer(duration, () { + resume.succeed(null); + }); + + if (resume._deferred.unsafeCompleted) { + timer.cancel(); + return resume._deferred.wait<Null>().match( + onFailure: (_) => fpdart_unit.unit, + onSuccess: (_) => fpdart_unit.unit, + ); + } + + return Effect.unit(); + }, + ); /// {@category constructors} factory Effect.raceAll(Iterable<Effect<E, L, R>> iterable) => @@ -189,14 +242,6 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { ); }); - /// {@category constructors} - static Effect<E, L, void> sleep<E, L>(Duration duration) => Effect.from( - (_) => Future.delayed( - duration, - () => const Right(null), - ), - ); - /// {@category constructors} static Effect<E, Never, Never> die<E>(dynamic defect) => Effect.from( (_) => Left(Die.current(defect)), diff --git a/packages/fpdart/test/src/effect/effect_constructors_test.dart b/packages/fpdart/test/src/effect/effect_constructors_test.dart index 81e6f67..def27d6 100644 --- a/packages/fpdart/test/src/effect/effect_constructors_test.dart +++ b/packages/fpdart/test/src/effect/effect_constructors_test.dart @@ -17,6 +17,44 @@ void main() { expect(result, "error"); }); + group('async', () { + test('succeed callback', () async { + final main = Effect<Null, String, int>.async( + (resume) => resume.succeed(10), + ); + final result = await main.runFuture(); + expect(result, 10); + }); + + test('fail callback', () async { + final main = Effect<Null, String, int>.async( + (resume) => resume.fail("error"), + ); + final result = await main.flip().runFuture(); + expect(result, "error"); + }); + + test('succeed async callback', () async { + final main = Effect<Null, String, int>.async( + (resume) => Future.delayed(Duration(milliseconds: 100)).then( + (_) => resume.succeed(10), + ), + ); + final result = await main.runFuture(); + expect(result, 10); + }); + + test('fail async callback', () async { + final main = Effect<Null, String, int>.async( + (resume) => Future.delayed(Duration(milliseconds: 100)).then( + (_) => resume.fail("error"), + ), + ); + final result = await main.flip().runFuture(); + expect(result, "error"); + }); + }); + group('tryCatch', () { test('executes once', () { var mutable = 0; From 2fe20e1c6195f51fb3823426c78dbd9ece3e0154 Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 7 Apr 2024 07:09:53 +0900 Subject: [PATCH 89/91] rename constructors sync/future --- examples/fpdart_http/lib/main.dart | 2 +- packages/fpdart/lib/src/effect.dart | 4 +-- packages/fpdart/test/src/deferred_test.dart | 2 +- .../src/effect/effect_alternatives_test.dart | 8 +++--- .../src/effect/effect_collecting_test.dart | 4 +-- .../src/effect/effect_constructors_test.dart | 26 +++++++++---------- .../src/effect/effect_do_notation_test.dart | 8 +++--- .../effect/effect_error_handling_test.dart | 2 +- .../test/src/effect/effect_scoping_test.dart | 10 +++---- .../src/effect/effect_sequencing_test.dart | 10 +++---- 10 files changed, 38 insertions(+), 38 deletions(-) diff --git a/examples/fpdart_http/lib/main.dart b/examples/fpdart_http/lib/main.dart index 320c721..3e58356 100644 --- a/examples/fpdart_http/lib/main.dart +++ b/examples/fpdart_http/lib/main.dart @@ -16,5 +16,5 @@ void main() async { .provideEnv( http.Client(), ) - .runFuture(); + .runFutureOrThrow(); } diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index d449989..9490c3b 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -777,7 +777,7 @@ extension ProvideNull<L, R> on Effect<Null, L, R> { ); /// {@category execution} - R runSync() { + R runSyncOrThrow() { try { final result = _unsafeRun(Context.env(null)); if (result is Future) { @@ -815,7 +815,7 @@ extension ProvideNull<L, R> on Effect<Null, L, R> { } /// {@category execution} - Future<R> runFuture() async { + Future<R> runFutureOrThrow() async { try { final result = _unsafeRun(Context.env(null)); if (result is! Future) { diff --git a/packages/fpdart/test/src/deferred_test.dart b/packages/fpdart/test/src/deferred_test.dart index a77bc58..be943bc 100644 --- a/packages/fpdart/test/src/deferred_test.dart +++ b/packages/fpdart/test/src/deferred_test.dart @@ -18,7 +18,7 @@ void main() { return value; }); - final result = await main.runFuture(); + final result = await main.runFutureOrThrow(); expect(result, 1); }); }); diff --git a/packages/fpdart/test/src/effect/effect_alternatives_test.dart b/packages/fpdart/test/src/effect/effect_alternatives_test.dart index 73dde6f..2b7ccb9 100644 --- a/packages/fpdart/test/src/effect/effect_alternatives_test.dart +++ b/packages/fpdart/test/src/effect/effect_alternatives_test.dart @@ -10,13 +10,13 @@ void main() { group('orDie', () { test('succeed', () { final main = Effect<Null, String, int>.succeed(10).orDie; - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, 10); }); test('fail', () { final main = Effect<Null, String, int>.fail("error").orDie; - expect(() => main.runSync(), throwsA(isA<Die>())); + expect(() => main.runSyncOrThrow(), throwsA(isA<Die>())); }); }); @@ -24,14 +24,14 @@ void main() { test('succeed', () { final main = Effect<Null, String, int>.succeed(10) .orDieWith((_) => CustomError()); - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, 10); }); test('fail', () { final main = Effect<Null, String, int>.fail("error") .orDieWith((_) => CustomError()); - expect(() => main.runSync(), throwsA(isA<Die>())); + expect(() => main.runSyncOrThrow(), throwsA(isA<Die>())); }); }); }, diff --git a/packages/fpdart/test/src/effect/effect_collecting_test.dart b/packages/fpdart/test/src/effect/effect_collecting_test.dart index dce3a81..5c3b74f 100644 --- a/packages/fpdart/test/src/effect/effect_collecting_test.dart +++ b/packages/fpdart/test/src/effect/effect_collecting_test.dart @@ -11,7 +11,7 @@ void main() { Effect.succeed(10), Effect.succeed(20), ]); - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, [10, 20]); }); @@ -23,7 +23,7 @@ void main() { Effect.succeedLazy(() => mutable += 1), Effect.fail("0"), ]); - final result = main.flip().runSync(); + final result = main.flip().runSyncOrThrow(); expect(mutable, 0); expect(result, "10"); }); diff --git a/packages/fpdart/test/src/effect/effect_constructors_test.dart b/packages/fpdart/test/src/effect/effect_constructors_test.dart index def27d6..b15d969 100644 --- a/packages/fpdart/test/src/effect/effect_constructors_test.dart +++ b/packages/fpdart/test/src/effect/effect_constructors_test.dart @@ -7,13 +7,13 @@ void main() { () { test('succeed', () { final main = Effect<Null, String, int>.succeed(10); - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, 10); }); test('fail', () { final main = Effect<Null, String, int>.fail("error"); - final result = main.flip().runSync(); + final result = main.flip().runSyncOrThrow(); expect(result, "error"); }); @@ -22,7 +22,7 @@ void main() { final main = Effect<Null, String, int>.async( (resume) => resume.succeed(10), ); - final result = await main.runFuture(); + final result = await main.runFutureOrThrow(); expect(result, 10); }); @@ -30,7 +30,7 @@ void main() { final main = Effect<Null, String, int>.async( (resume) => resume.fail("error"), ); - final result = await main.flip().runFuture(); + final result = await main.flip().runFutureOrThrow(); expect(result, "error"); }); @@ -40,7 +40,7 @@ void main() { (_) => resume.succeed(10), ), ); - final result = await main.runFuture(); + final result = await main.runFutureOrThrow(); expect(result, 10); }); @@ -50,7 +50,7 @@ void main() { (_) => resume.fail("error"), ), ); - final result = await main.flip().runFuture(); + final result = await main.flip().runFutureOrThrow(); expect(result, "error"); }); }); @@ -66,7 +66,7 @@ void main() { onError: (error, stackTrace) {}, ); - main.runSync(); + main.runSyncOrThrow(); expect(mutable, 1); }); @@ -75,7 +75,7 @@ void main() { execute: () async => 10, onError: (error, stackTrace) {}, ); - final result = await main.runFuture(); + final result = await main.runFutureOrThrow(); expect(result, 10); }); @@ -84,7 +84,7 @@ void main() { execute: () => 10, onError: (error, stackTrace) {}, ); - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, 10); }); }); @@ -95,7 +95,7 @@ void main() { final value = $.sync(Effect.succeed(10)); return value; }); - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, 10); }); @@ -104,7 +104,7 @@ void main() { final value = $.sync(Effect.fail("abc")); return value; }); - final result = main.flip().runSync(); + final result = main.flip().runSyncOrThrow(); expect(result, "abc"); }); @@ -114,7 +114,7 @@ void main() { await $.async(Effect.succeedLazy(() => Future.value(10))); return value; }); - final result = await main.runFuture(); + final result = await main.runFutureOrThrow(); expect(result, 10); }); @@ -126,7 +126,7 @@ void main() { return value; }); - expect(() => main.runSync(), throwsA(isA<Die>())); + expect(() => main.runSyncOrThrow(), throwsA(isA<Die>())); }); }); }, diff --git a/packages/fpdart/test/src/effect/effect_do_notation_test.dart b/packages/fpdart/test/src/effect/effect_do_notation_test.dart index 28f199d..ff3be2e 100644 --- a/packages/fpdart/test/src/effect/effect_do_notation_test.dart +++ b/packages/fpdart/test/src/effect/effect_do_notation_test.dart @@ -13,7 +13,7 @@ void main() { }); final program = main.provideEnv("abc"); - final result = program.runSync(); + final result = program.runSyncOrThrow(); expect(result, 3); }); }); @@ -27,7 +27,7 @@ void main() { final program = main.provideEffect(Effect<Null, String, int>.succeed(10)); - final result = program.runSync(); + final result = program.runSyncOrThrow(); expect(result, 11); }); @@ -39,7 +39,7 @@ void main() { final program = main.provideEffect(Effect<Null, String, int>.fail("error")); - final result = program.flip().runSync(); + final result = program.flip().runSyncOrThrow(); expect(result, "error"); }); }); @@ -54,7 +54,7 @@ void main() { return value; }); - final result = main.provideEnv("abc").runSync(); + final result = main.provideEnv("abc").runSyncOrThrow(); expect(result, 4); }); }); diff --git a/packages/fpdart/test/src/effect/effect_error_handling_test.dart b/packages/fpdart/test/src/effect/effect_error_handling_test.dart index 921fc7c..c2931d6 100644 --- a/packages/fpdart/test/src/effect/effect_error_handling_test.dart +++ b/packages/fpdart/test/src/effect/effect_error_handling_test.dart @@ -13,7 +13,7 @@ void main() { .catchCause( (cause) => Effect.succeed("abc"), ) - .runSync(); + .runSyncOrThrow(); expect(result, "abc"); }); diff --git a/packages/fpdart/test/src/effect/effect_scoping_test.dart b/packages/fpdart/test/src/effect/effect_scoping_test.dart index 54148e9..b01ad88 100644 --- a/packages/fpdart/test/src/effect/effect_scoping_test.dart +++ b/packages/fpdart/test/src/effect/effect_scoping_test.dart @@ -16,7 +16,7 @@ void main() { }), ).provideScope; - await main.runFuture(); + await main.runFutureOrThrow(); expect(mutable, 1); }); @@ -30,9 +30,9 @@ void main() { }), ); - await main.provide(Context.env(scope)).runFuture(); + await main.provide(Context.env(scope)).runFutureOrThrow(); expect(mutable, 0); - scope.closeScope<Null, Never>().runSync(); + scope.closeScope<Null, Never>().runSyncOrThrow(); expect(mutable, 1); }); @@ -48,7 +48,7 @@ void main() { ) .provideScope; - await main.runFuture(); + await main.runFutureOrThrow(); expect(mutable, 10); }); @@ -63,7 +63,7 @@ void main() { ) .provideScope; - await main.flip().runFuture(); + await main.flip().runFutureOrThrow(); expect(mutable, 0); }); }); diff --git a/packages/fpdart/test/src/effect/effect_sequencing_test.dart b/packages/fpdart/test/src/effect/effect_sequencing_test.dart index 48a5598..750d02e 100644 --- a/packages/fpdart/test/src/effect/effect_sequencing_test.dart +++ b/packages/fpdart/test/src/effect/effect_sequencing_test.dart @@ -8,14 +8,14 @@ void main() { test('zipLeft', () { final main = Effect<Null, String, int>.succeed(10).zipLeft(Effect.succeed("10")); - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, 10); }); test('zipRight', () { final main = Effect<Null, String, int>.succeed(10) .zipRight(Effect.succeed("10")); - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, "10"); }); @@ -28,7 +28,7 @@ void main() { ); expect(mutable, 0); - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, 10); expect(mutable, 1); }); @@ -40,7 +40,7 @@ void main() { final second = Effect.sleep<Null, String>(Duration(milliseconds: 100)) .map((_) => 2); - final result = await first.race(second).runFuture(); + final result = await first.race(second).runFutureOrThrow(); expect(result, 1); }); @@ -50,7 +50,7 @@ void main() { final second = Effect.sleep<Null, String>(Duration(milliseconds: 50)) .map((_) => 2); - final result = await first.race(second).runFuture(); + final result = await first.race(second).runFutureOrThrow(); expect(result, 2); }); }); From 40399391f05a422ce1371ba88247f0d25f86bebb Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Sun, 7 Apr 2024 10:48:40 +0900 Subject: [PATCH 90/91] example read/write file --- examples/file_read_stream/.gitignore | 1 + .../file_read_stream/analysis_options.yaml | 12 + examples/file_read_stream/lib/main.dart | 130 + examples/file_read_stream/lib/word.dart | 53 + examples/file_read_stream/lib/word_pairs.csv | 6377 +++++++++++++ examples/file_read_stream/lib/words.csv | 7971 +++++++++++++++++ examples/file_read_stream/pubspec.yaml | 20 + packages/fpdart/lib/fpdart.dart | 4 + 8 files changed, 14568 insertions(+) create mode 100644 examples/file_read_stream/.gitignore create mode 100644 examples/file_read_stream/analysis_options.yaml create mode 100644 examples/file_read_stream/lib/main.dart create mode 100644 examples/file_read_stream/lib/word.dart create mode 100644 examples/file_read_stream/lib/word_pairs.csv create mode 100644 examples/file_read_stream/lib/words.csv create mode 100644 examples/file_read_stream/pubspec.yaml diff --git a/examples/file_read_stream/.gitignore b/examples/file_read_stream/.gitignore new file mode 100644 index 0000000..66e4ee6 --- /dev/null +++ b/examples/file_read_stream/.gitignore @@ -0,0 +1 @@ +lib/output.json \ No newline at end of file diff --git a/examples/file_read_stream/analysis_options.yaml b/examples/file_read_stream/analysis_options.yaml new file mode 100644 index 0000000..027c612 --- /dev/null +++ b/examples/file_read_stream/analysis_options.yaml @@ -0,0 +1,12 @@ +include: package:lints/recommended.yaml + +linter: + rules: + annotate_overrides: true + prefer_void_to_null: false + +analyzer: + language: + strict-casts: true + strict-inference: true + strict-raw-types: true diff --git a/examples/file_read_stream/lib/main.dart b/examples/file_read_stream/lib/main.dart new file mode 100644 index 0000000..b8350a1 --- /dev/null +++ b/examples/file_read_stream/lib/main.dart @@ -0,0 +1,130 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:fpdart/fpdart.dart'; + +import './word.dart'; + +typedef Env = ({ + String wordPairCsv, + String wordsCsv, + String outputPath, +}); + +final wordPairs = Effect<Env, String, List<WordPair>>.gen(($) async { + final env = $.sync(Effect.env()); + final inputFile = File(env.wordPairCsv); + + final lines = inputFile.openRead().transform(utf8.decoder).transform( + LineSplitter(), + ); + + final wordCollection = <WordPair>[]; + await for (var line in lines) { + final split = line.split(','); + if (split.length != 3) { + return $.sync(Effect.fail("Missing word-pair info at: '$line'")); + } + + final wordInfo = $ + .sync(Effect.all([ + Effect.fromNullable( + int.tryParse(split[0]), + onNull: () => "Missing id collection", + ), + Effect.fromNullable( + int.tryParse(split[1]), + onNull: () => "Missing id word1", + ), + Effect.fromNullable( + int.tryParse(split[2]), + onNull: () => "Missing id word2", + ), + ])) + .toList(); + + wordCollection.add(WordPair(wordInfo[0], wordInfo[1], wordInfo[2])); + } + + return wordCollection; +}); + +final words = Effect<Env, String, List<WordFull>>.gen(($) async { + final env = $.sync(Effect.env()); + final wordCollection = await $.async(wordPairs); + + final inputFile = File(env.wordsCsv); + final lines = inputFile.openRead().transform(utf8.decoder).transform( + LineSplitter(), + ); + + final wordMap = <int, String>{}; + await for (var line in lines) { + final split = line.split(','); + if (split.length < 2) { + return $.sync(Effect.fail("Missing word info at: '$line'")); + } + + final idWord = $.sync(Effect.fromNullable( + int.tryParse(split[0]), + onNull: () => "Missing id word", + )); + final word = split[1]; + + wordMap[idWord] = word; + } + + final wordFullList = <WordFull>[]; + for (var entry in wordCollection) { + final word1 = $.sync(Effect.fromNullable( + wordMap[entry.idWord1], + onNull: () => "Missing word 1 at: $entry", + )); + final word2 = $.sync(Effect.fromNullable( + wordMap[entry.idWord2], + onNull: () => "Missing word 2 at: $entry", + )); + + wordFullList.add( + WordFull( + entry.idCollection, + Word(entry.idWord1, word1), + Word(entry.idWord2, word2), + ), + ); + } + + return wordFullList.toSet().toList(); +}); + +final program = Effect<Env, String, Unit>.gen(($) async { + final env = $.sync(Effect.env()); + final wordFullList = await $.async(words); + + final outputFile = File(env.outputPath); + await $.async( + Effect.tryCatch( + execute: () => outputFile.writeAsString( + "[${wordFullList.map((e) => e.toJson()).join(",\n")}]", + ), + onError: (_, __) => "Error while writing output file", + ), + ); + + return unit; +}); + +void main() async { + await program.provideEnv(( + wordPairCsv: "./lib/word_pairs.csv", + wordsCsv: "./lib/words.csv", + outputPath: "./lib/output.json", + )).matchCause( + onFailure: (cause) { + print(cause); + }, + onSuccess: (_) { + print("Success"); + }, + ).runFutureExit(); +} diff --git a/examples/file_read_stream/lib/word.dart b/examples/file_read_stream/lib/word.dart new file mode 100644 index 0000000..80e2d1b --- /dev/null +++ b/examples/file_read_stream/lib/word.dart @@ -0,0 +1,53 @@ +import 'package:equatable/equatable.dart'; + +class WordPair { + final int idCollection; + final int idWord1; + final int idWord2; + const WordPair(this.idCollection, this.idWord1, this.idWord2); + + @override + String toString() { + return "($idCollection)$idWord1|$idWord2"; + } +} + +class Word extends Equatable { + final int idWord; + final String word; + const Word(this.idWord, this.word); + + @override + String toString() { + return "($idWord)$word"; + } + + @override + List<Object?> get props => [idWord]; + + Map<String, dynamic> toJson() => { + '"idWord"': idWord, + '"word"': '"$word"', + }; +} + +class WordFull extends Equatable { + final int idCollection; + final Word word1; + final Word word2; + const WordFull(this.idCollection, this.word1, this.word2); + + @override + String toString() { + return "👉 $idCollection\n $word1\n $word2"; + } + + @override + List<Object?> get props => [word1, word2]; + + Map<String, dynamic> toJson() => { + '"idCollection"': idCollection, + '"word1"': word1.toJson(), + '"word2"': word2.toJson(), + }; +} diff --git a/examples/file_read_stream/lib/word_pairs.csv b/examples/file_read_stream/lib/word_pairs.csv new file mode 100644 index 0000000..1bee4c6 --- /dev/null +++ b/examples/file_read_stream/lib/word_pairs.csv @@ -0,0 +1,6377 @@ +1,1,2048 +1,2,2049 +1,3,2050 +1,4,2051 +1,5,2052 +1,6,2053 +1,7,2054 +1,8,2055 +57,8,2055 +168,8,2055 +1,9,2056 +1,10,2057 +1,11,2058 +1,12,2059 +1,13,2060 +181,13,2060 +1,14,2061 +90,14,2061 +1,15,2062 +44,15,2062 +1,16,2063 +57,16,2063 +1,17,2064 +1,18,2065 +57,18,2065 +1,19,2066 +1,20,2067 +57,20,2067 +1,21,2068 +1,22,2069 +1,23,2070 +57,23,2070 +1,24,2071 +1,25,2072 +1,26,2073 +1,27,2074 +38,27,2074 +220,27,2074 +1,28,2075 +1,29,2076 +81,29,2076 +159,29,2076 +2,30,2077 +144,30,2077 +2,31,2078 +2,32,2079 +2,33,2080 +2,34,2081 +2,35,2082 +2,36,2083 +171,36,2083 +2,37,2084 +103,37,2084 +2,38,2085 +105,38,2085 +150,38,2085 +2,39,2086 +57,39,2086 +2,40,2087 +2,41,2088 +164,41,2088 +2,42,2089 +154,42,2089 +2,43,2090 +125,43,2090 +2,44,2091 +2,45,2092 +191,45,2092 +2,46,2093 +2,47,2094 +57,47,2094 +2,48,2095 +159,48,2095 +2,49,2096 +2,50,2097 +2,51,2098 +2,52,2099 +49,52,2099 +2,53,2100 +44,53,2100 +2,54,2101 +171,54,2101 +2,55,2102 +2,56,2103 +50,56,2103 +168,56,2103 +210,56,2103 +2,57,2104 +41,57,2104 +57,57,2104 +2,58,2105 +38,58,2105 +2,59,2106 +215,59,2106 +3,60,2107 +41,60,2107 +164,60,2107 +3,61,2108 +119,61,2108 +153,61,2108 +3,62,2109 +57,62,2109 +160,62,2109 +3,63,2110 +3,64,2111 +3,65,2112 +185,65,2112 +3,66,2113 +91,66,2113 +152,66,2113 +3,67,2114 +147,67,2114 +3,68,2115 +3,69,2116 +227,69,2116 +3,70,2117 +44,70,2117 +152,70,2117 +3,71,2118 +3,72,2119 +159,72,2119 +3,73,2120 +3,74,2121 +39,74,2121 +128,74,2121 +3,75,2122 +57,75,2122 +3,76,2123 +110,76,2123 +3,77,2124 +3,78,2125 +3,79,2126 +3,80,2127 +3,81,2128 +110,81,2128 +128,81,2128 +3,82,2129 +3,83,2130 +212,83,2130 +3,84,2131 +3,85,2132 +57,85,2132 +220,85,2132 +3,86,2133 +66,86,2133 +3,87,2134 +57,87,2134 +71,87,2134 +230,87,2134 +3,88,2135 +49,88,2135 +3,89,2136 +4,90,2137 +155,90,2137 +4,91,2138 +35,91,2138 +69,91,2138 +4,92,2139 +4,93,2140 +51,93,2140 +164,93,2140 +203,93,2140 +4,94,2141 +4,95,2142 +34,95,2142 +153,95,2142 +4,96,2143 +162,96,2143 +4,97,2144 +48,97,2144 +147,97,2144 +4,98,2145 +4,99,2146 +57,99,2146 +4,100,2147 +135,100,2147 +4,101,2148 +121,101,2148 +226,101,2148 +4,102,2149 +105,102,2149 +4,103,2150 +128,103,2150 +4,104,2151 +79,104,2151 +153,104,2151 +4,105,2152 +42,105,2152 +142,105,2152 +4,106,2153 +4,107,2154 +90,107,2154 +4,108,2155 +4,109,2156 +4,110,2157 +102,110,2157 +148,110,2157 +4,111,2158 +76,111,2158 +4,112,2159 +47,112,2159 +142,112,2159 +4,113,2160 +68,113,2160 +4,114,2161 +158,114,2161 +4,115,2162 +139,115,2162 +4,116,2163 +4,117,2164 +57,117,2164 +117,117,2164 +4,118,2165 +46,118,2165 +57,118,2165 +160,118,2165 +4,119,2166 +57,119,2166 +122,119,2166 +5,120,2167 +5,121,2168 +39,121,2168 +159,121,2168 +5,122,2169 +39,122,2169 +158,122,2169 +5,123,2170 +62,123,2170 +5,124,2171 +159,124,2171 +5,125,2172 +63,125,2172 +5,126,2173 +5,127,2174 +70,127,2174 +5,128,2175 +77,128,2175 +5,129,2176 +57,129,2176 +5,130,2177 +36,130,2177 +170,130,2177 +5,131,2178 +37,131,2178 +125,131,2178 +5,132,2179 +5,133,2180 +92,133,2180 +164,133,2180 +5,134,2181 +5,135,2182 +46,135,2182 +183,135,2182 +5,136,2183 +40,136,2183 +129,136,2183 +5,137,2184 +49,137,2184 +5,138,2185 +5,139,2186 +5,140,2187 +130,140,2187 +5,141,2188 +90,141,2188 +194,141,2188 +5,142,2189 +48,142,2189 +154,142,2189 +218,142,2189 +5,143,2190 +47,143,2190 +5,144,2191 +62,144,2191 +5,145,2192 +5,146,2193 +105,146,2193 +193,146,2193 +5,147,2194 +5,148,2195 +90,148,2195 +166,148,2195 +5,149,2196 +6,150,2197 +57,150,2197 +6,151,2198 +38,151,2198 +6,152,2199 +6,153,2200 +92,153,2200 +165,153,2200 +6,154,2201 +6,155,2202 +122,155,2202 +6,156,2203 +157,156,2203 +6,157,2204 +64,157,2204 +6,158,2205 +61,158,2205 +6,159,2206 +96,159,2206 +6,160,2207 +123,160,2207 +6,161,2208 +160,161,2208 +6,162,2209 +62,162,2209 +126,162,2209 +6,163,2210 +41,163,2210 +6,164,2211 +175,164,2211 +217,164,2211 +6,165,2212 +168,165,2212 +219,165,2212 +6,166,2213 +6,167,2214 +6,168,2215 +37,168,2215 +6,169,2216 +175,169,2216 +6,170,2217 +233,170,2217 +6,171,2218 +123,171,2218 +127,171,2218 +6,172,2219 +68,172,2219 +6,173,2220 +6,174,2221 +178,174,2221 +6,175,2222 +90,175,2222 +6,176,2223 +174,176,2223 +6,177,2224 +6,178,2225 +6,179,2226 +35,179,2226 +147,179,2226 +7,180,2227 +83,180,2227 +211,180,2227 +7,181,2228 +7,182,2229 +35,182,2229 +142,182,2229 +7,183,2230 +178,183,2230 +7,184,2231 +72,184,2231 +7,185,2232 +84,185,2232 +150,185,2232 +7,186,2233 +70,186,2233 +168,186,2233 +7,187,2234 +169,187,2234 +7,188,2235 +34,188,2235 +57,188,2235 +155,188,2235 +7,189,2236 +57,189,2236 +71,189,2236 +140,189,2236 +7,190,2237 +79,190,2237 +7,191,2238 +90,191,2238 +123,191,2238 +7,192,2239 +64,192,2239 +127,192,2239 +7,193,2240 +175,193,2240 +7,194,2241 +226,194,2241 +7,195,2242 +92,195,2242 +7,196,2243 +7,197,2244 +7,198,2245 +198,198,2245 +200,198,2245 +7,199,2246 +96,199,2246 +7,200,2247 +39,200,2247 +7,201,2248 +39,201,2248 +65,201,2248 +7,202,2249 +65,202,2249 +203,202,2249 +7,203,2250 +81,203,2250 +168,203,2250 +7,204,2251 +7,205,2252 +109,205,2252 +7,206,2253 +127,206,2253 +7,207,2254 +36,207,2254 +57,207,2254 +180,207,2254 +7,208,2255 +115,208,2255 +7,209,2256 +36,209,2256 +55,209,2256 +8,210,2257 +8,211,2258 +165,211,2258 +8,212,2259 +81,212,2259 +209,212,2259 +8,213,2260 +8,214,2261 +40,214,2261 +130,214,2261 +8,215,2262 +41,215,2262 +8,216,2263 +8,217,2264 +45,217,2264 +8,218,2265 +8,219,2266 +8,220,2267 +60,220,2267 +8,221,2268 +162,221,2268 +8,222,2269 +90,222,2269 +179,222,2269 +8,223,2270 +117,223,2270 +8,224,2271 +65,224,2271 +8,225,2272 +51,225,2272 +8,226,2273 +42,226,2273 +8,227,2274 +8,228,2275 +83,228,2275 +8,229,2276 +42,229,2276 +8,230,2277 +8,231,2278 +62,231,2278 +179,231,2278 +8,232,2279 +8,233,2280 +8,234,2281 +67,234,2281 +135,234,2281 +8,235,2282 +8,236,2283 +44,236,2283 +164,236,2283 +8,237,2284 +96,237,2284 +8,238,2285 +8,239,2286 +9,240,2287 +147,240,2287 +9,241,2288 +9,242,2289 +87,242,2289 +201,242,2289 +9,243,2290 +9,244,2291 +37,244,2291 +144,244,2291 +9,245,2292 +43,245,2292 +57,245,2292 +9,246,2293 +89,246,2293 +9,247,2294 +215,247,2294 +9,248,2295 +9,249,2296 +41,249,2296 +9,250,2297 +9,251,2298 +72,251,2298 +9,252,2299 +110,252,2299 +230,252,2299 +9,253,2300 +9,254,2301 +57,254,2301 +9,255,2302 +41,255,2302 +57,255,2302 +9,256,2303 +82,256,2303 +9,257,2304 +60,257,2304 +152,257,2304 +9,258,2305 +49,258,2305 +9,259,2306 +78,259,2306 +173,259,2306 +9,260,2307 +9,261,2308 +9,262,2309 +41,262,2309 +9,263,2310 +9,264,2311 +173,264,2311 +9,265,2312 +125,265,2312 +9,266,2313 +115,266,2313 +9,267,2314 +43,267,2314 +160,267,2314 +9,268,2315 +58,268,2315 +66,268,2315 +9,269,2316 +218,269,2316 +10,270,2317 +230,270,2317 +10,271,2318 +10,272,2319 +169,272,2319 +10,273,2320 +10,274,2321 +10,275,2322 +10,276,2323 +10,277,2324 +10,278,2325 +61,278,2325 +10,279,2326 +10,280,2327 +10,281,2328 +159,281,2328 +10,282,2329 +95,282,2329 +10,283,2330 +231,283,2330 +10,284,2331 +49,284,2331 +10,285,2332 +10,286,2333 +61,286,2333 +10,287,2334 +10,288,2335 +10,289,2336 +66,289,2336 +10,290,2337 +10,291,2338 +10,292,2339 +10,293,2340 +223,293,2340 +10,294,2341 +169,294,2341 +10,295,2342 +10,296,2343 +10,297,2344 +128,297,2344 +10,298,2345 +10,299,2346 +159,299,2346 +11,300,2347 +11,301,2348 +11,302,2349 +79,302,2349 +11,303,2350 +79,303,2350 +11,304,2351 +11,305,2352 +163,305,2352 +11,306,2353 +91,306,2353 +11,307,2354 +122,307,2354 +11,308,2355 +11,309,2356 +192,309,2356 +11,310,2357 +11,311,2358 +51,311,2358 +11,312,2359 +76,312,2359 +11,313,2360 +178,313,2360 +1,314,2361 +11,314,2361 +11,315,2362 +11,316,2363 +11,317,2364 +169,317,2364 +11,318,2365 +11,319,2366 +60,319,2366 +11,320,2367 +11,321,2368 +82,321,2368 +11,322,2369 +11,323,2370 +128,323,2370 +11,324,2371 +168,324,2371 +11,325,2372 +178,325,2372 +11,326,2373 +11,327,2374 +167,327,2374 +192,327,2374 +11,328,2375 +89,328,2375 +11,329,2376 +12,330,2377 +12,331,2378 +12,332,2379 +12,333,2380 +12,334,2381 +12,335,2382 +12,336,2383 +12,337,2384 +78,337,2384 +12,338,2385 +60,338,2385 +214,338,2385 +12,339,2386 +34,339,2386 +181,339,2386 +228,339,2386 +12,340,2387 +151,340,2387 +12,341,2388 +156,341,2388 +12,342,2389 +168,342,2389 +12,343,2390 +95,343,2390 +229,343,2390 +12,344,2391 +149,344,2391 +12,345,2392 +12,346,2393 +12,347,2394 +205,347,2394 +12,348,2395 +207,348,2395 +12,349,2396 +12,350,2397 +128,350,2397 +12,351,2398 +128,351,2398 +176,351,2398 +12,352,2399 +181,352,2399 +223,352,2399 +12,353,2400 +67,353,2400 +12,354,2401 +61,354,2401 +152,354,2401 +12,355,2402 +87,355,2402 +12,356,2403 +72,356,2403 +12,357,2404 +107,357,2404 +12,358,2405 +118,358,2405 +180,358,2405 +12,359,2406 +13,360,2407 +13,361,2408 +107,361,2408 +13,362,2409 +181,362,2409 +13,363,2410 +39,363,2410 +203,363,2410 +13,364,2411 +13,365,2412 +160,365,2412 +13,366,2413 +13,367,2414 +161,367,2414 +13,368,2415 +42,368,2415 +147,368,2415 +13,369,2416 +188,369,2416 +13,370,2417 +188,370,2417 +13,371,2418 +13,372,2419 +35,372,2419 +130,372,2419 +13,373,2420 +51,373,2420 +165,373,2420 +13,374,2421 +13,375,2422 +13,376,2423 +149,376,2423 +13,377,2424 +13,378,2425 +13,379,2426 +63,379,2426 +138,379,2426 +225,379,2426 +13,380,2427 +13,381,2428 +95,381,2428 +13,382,2429 +81,382,2429 +13,383,2430 +57,383,2430 +70,383,2430 +13,384,2431 +13,385,2432 +78,385,2432 +13,386,2433 +165,386,2433 +13,387,2434 +61,387,2434 +211,387,2434 +13,388,2435 +38,388,2435 +13,389,2436 +14,390,2437 +40,390,2437 +47,390,2437 +14,391,2438 +50,391,2438 +14,392,2439 +14,393,2440 +43,393,2440 +14,394,2441 +96,394,2441 +14,395,2442 +216,395,2442 +14,396,2443 +108,396,2443 +174,396,2443 +14,397,2444 +46,397,2444 +69,397,2444 +14,398,2445 +45,398,2445 +14,399,2446 +14,400,2447 +87,400,2447 +156,400,2447 +14,401,2448 +39,401,2448 +170,401,2448 +14,402,2449 +34,402,2449 +75,402,2449 +14,403,2450 +175,403,2450 +14,404,2451 +72,404,2451 +14,405,2452 +116,405,2452 +14,406,2453 +207,406,2453 +14,407,2454 +82,407,2454 +132,407,2454 +232,407,2454 +14,408,2455 +46,408,2455 +148,408,2455 +14,409,2456 +63,409,2456 +173,409,2456 +14,410,2457 +42,410,2457 +212,410,2457 +14,411,2458 +44,411,2458 +207,411,2458 +14,412,2459 +60,412,2459 +14,413,2460 +146,413,2460 +14,414,2461 +95,414,2461 +14,415,2462 +73,415,2462 +14,416,2463 +14,417,2464 +55,417,2464 +129,417,2464 +14,418,2465 +14,419,2466 +35,419,2466 +164,419,2466 +15,420,2467 +15,421,2468 +66,421,2468 +162,421,2468 +15,422,2469 +164,422,2469 +15,423,2470 +57,423,2470 +15,424,2471 +95,424,2471 +15,425,2472 +15,426,2473 +15,427,2474 +142,427,2474 +15,428,2475 +142,428,2475 +15,429,2476 +87,429,2476 +15,430,2477 +80,430,2477 +15,431,2478 +150,431,2478 +15,432,2479 +140,432,2479 +15,433,2480 +166,433,2480 +15,434,2481 +35,434,2481 +15,435,2482 +58,435,2482 +15,436,2483 +111,436,2483 +15,437,2484 +40,437,2484 +15,438,2485 +213,438,2485 +15,439,2486 +184,439,2486 +15,440,2487 +207,440,2487 +15,441,2488 +106,441,2488 +15,442,2489 +15,443,2490 +106,443,2490 +15,444,2491 +15,445,2492 +15,446,2493 +15,447,2494 +15,448,2495 +35,448,2495 +57,448,2495 +189,448,2495 +15,449,2496 +95,449,2496 +16,450,2497 +136,450,2497 +163,450,2497 +16,451,2498 +39,451,2498 +16,452,2499 +48,452,2499 +16,453,2500 +95,453,2500 +16,454,2501 +51,454,2501 +16,455,2502 +48,455,2502 +70,455,2502 +16,456,2503 +38,456,2503 +57,456,2503 +183,456,2503 +16,457,2504 +39,457,2504 +16,458,2505 +80,458,2505 +183,458,2505 +16,459,2506 +77,459,2506 +16,460,2507 +78,460,2507 +16,461,2508 +213,461,2508 +16,462,2509 +68,462,2509 +16,463,2510 +130,463,2510 +16,464,2511 +171,464,2511 +16,465,2512 +128,465,2512 +16,466,2513 +44,466,2513 +16,467,2514 +47,467,2514 +16,468,2515 +36,468,2515 +16,469,2516 +36,469,2516 +58,469,2516 +16,470,2517 +95,470,2517 +16,471,2518 +16,472,2519 +43,472,2519 +16,473,2520 +129,473,2520 +16,474,2521 +65,474,2521 +159,474,2521 +16,475,2522 +45,475,2522 +126,475,2522 +16,476,2523 +163,476,2523 +16,477,2524 +107,477,2524 +16,478,2525 +57,478,2525 +58,478,2525 +148,478,2525 +223,478,2525 +16,479,2526 +44,479,2526 +17,480,2527 +41,480,2527 +17,481,2528 +70,481,2528 +156,481,2528 +17,482,2529 +36,482,2529 +149,482,2529 +17,483,2530 +17,484,2531 +47,484,2531 +203,484,2531 +17,485,2532 +34,485,2532 +57,485,2532 +176,485,2532 +17,486,2533 +17,487,2534 +58,487,2534 +162,487,2534 +222,487,2534 +17,488,2535 +17,489,2536 +17,490,2537 +62,490,2537 +17,491,2538 +48,491,2538 +17,492,2539 +73,492,2539 +17,493,2540 +149,493,2540 +17,494,2541 +145,494,2541 +17,495,2542 +107,495,2542 +17,496,2543 +17,497,2544 +91,497,2544 +17,498,2545 +149,498,2545 +17,499,2546 +213,499,2546 +17,500,2547 +48,500,2547 +173,500,2547 +17,501,2548 +95,501,2548 +187,501,2548 +17,502,2549 +17,503,2550 +82,503,2550 +17,504,2551 +17,505,2552 +86,505,2552 +17,506,2553 +35,506,2553 +157,506,2553 +17,507,2554 +17,508,2555 +65,508,2555 +17,509,2556 +61,509,2556 +153,509,2556 +18,510,2557 +35,510,2557 +57,510,2557 +18,511,2558 +118,511,2558 +153,511,2558 +18,512,2559 +148,512,2559 +18,513,2560 +132,513,2560 +18,514,2561 +18,515,2562 +35,515,2562 +169,515,2562 +18,516,2563 +57,516,2563 +18,517,2564 +40,517,2564 +144,517,2564 +18,518,2565 +73,518,2565 +18,519,2566 +57,519,2566 +64,519,2566 +18,520,2567 +76,520,2567 +156,520,2567 +18,521,2568 +70,521,2568 +211,521,2568 +18,522,2569 +40,522,2569 +132,522,2569 +18,523,2570 +39,523,2570 +18,524,2571 +72,524,2571 +18,525,2572 +18,526,2573 +34,526,2573 +177,526,2573 +18,527,2574 +40,527,2574 +177,527,2574 +18,528,2575 +22,528,2575 +34,528,2575 +18,529,2576 +18,530,2577 +204,530,2577 +18,531,2578 +46,531,2578 +154,531,2578 +18,532,2579 +18,533,2580 +47,533,2580 +149,533,2580 +18,534,2581 +47,534,2581 +160,534,2581 +18,535,2582 +78,535,2582 +18,536,2583 +80,536,2583 +208,536,2583 +18,537,2584 +36,537,2584 +181,537,2584 +18,538,2585 +41,538,2585 +18,539,2586 +19,540,2587 +187,540,2587 +19,541,2588 +77,541,2588 +19,542,2589 +19,543,2590 +38,543,2590 +125,543,2590 +19,544,2591 +118,544,2591 +19,545,2592 +59,545,2592 +178,545,2592 +19,546,2593 +58,546,2593 +160,546,2593 +19,547,2594 +95,547,2594 +146,547,2594 +19,548,2595 +109,548,2595 +19,549,2596 +19,550,2597 +19,551,2598 +65,551,2598 +19,552,2599 +105,552,2599 +19,553,2600 +98,553,2600 +181,553,2600 +19,554,2601 +19,555,2602 +45,555,2602 +19,556,2603 +134,556,2603 +19,557,2604 +66,557,2604 +141,557,2604 +19,558,2605 +132,558,2605 +19,559,2606 +113,559,2606 +208,559,2606 +19,560,2607 +45,560,2607 +19,561,2608 +71,561,2608 +186,561,2608 +19,562,2609 +63,562,2609 +19,563,2610 +92,563,2610 +141,563,2610 +19,564,2611 +49,564,2611 +19,565,2612 +38,565,2612 +57,565,2612 +177,565,2612 +19,566,2613 +98,566,2613 +144,566,2613 +19,567,2614 +43,567,2614 +139,567,2614 +19,568,2615 +57,568,2615 +64,568,2615 +229,568,2615 +19,569,2616 +104,569,2616 +166,569,2616 +20,570,2617 +64,570,2617 +20,571,2618 +109,571,2618 +20,572,2619 +20,573,2620 +145,573,2620 +20,574,2621 +42,574,2621 +20,575,2622 +50,575,2622 +187,575,2622 +20,576,2623 +20,577,2624 +71,577,2624 +20,578,2625 +20,579,2626 +141,579,2626 +20,580,2627 +160,580,2627 +187,580,2627 +20,581,2628 +128,581,2628 +145,581,2628 +20,582,2629 +98,582,2629 +20,583,2630 +83,583,2630 +20,584,2631 +94,584,2631 +20,585,2632 +130,585,2632 +20,586,2633 +72,586,2633 +20,587,2634 +145,587,2634 +20,588,2635 +36,588,2635 +20,589,2636 +96,589,2636 +20,590,2637 +34,590,2637 +20,591,2638 +20,592,2639 +20,593,2640 +141,593,2640 +20,594,2641 +115,594,2641 +20,595,2642 +94,595,2642 +226,595,2642 +20,596,2643 +41,596,2643 +176,596,2643 +20,597,2644 +20,598,2645 +91,598,2645 +20,599,2646 +101,599,2646 +21,600,2647 +43,600,2647 +129,600,2647 +21,601,2648 +73,601,2648 +225,601,2648 +21,602,2649 +189,602,2649 +21,603,2650 +21,604,2651 +118,604,2651 +220,604,2651 +21,605,2652 +188,605,2652 +21,606,2653 +145,606,2653 +21,607,2654 +167,607,2654 +21,608,2655 +83,608,2655 +136,608,2655 +21,609,2656 +63,609,2656 +182,609,2656 +21,610,2657 +81,610,2657 +203,610,2657 +21,611,2658 +40,611,2658 +139,611,2658 +21,612,2659 +72,612,2659 +21,613,2660 +21,614,2661 +66,614,2661 +167,614,2661 +21,615,2662 +101,615,2662 +21,616,2663 +58,616,2663 +21,617,2664 +62,617,2664 +142,617,2664 +21,618,2665 +58,618,2665 +21,619,2666 +125,619,2666 +21,620,2667 +21,621,2668 +132,621,2668 +21,622,2669 +116,622,2669 +197,622,2669 +199,622,2669 +21,623,2670 +50,623,2670 +21,624,2671 +186,624,2671 +21,625,2672 +59,625,2672 +150,625,2672 +21,626,2673 +88,626,2673 +21,627,2674 +38,627,2674 +144,627,2674 +21,628,2675 +215,628,2675 +21,629,2676 +22,630,2677 +100,630,2677 +176,630,2677 +22,631,2678 +145,631,2678 +22,632,2679 +102,632,2679 +173,632,2679 +22,633,2680 +22,634,2681 +151,634,2681 +228,634,2681 +22,635,2682 +221,635,2682 +22,636,2683 +153,636,2683 +22,637,2684 +74,637,2684 +22,638,2685 +22,639,2686 +121,639,2686 +22,640,2687 +107,640,2687 +182,640,2687 +22,641,2688 +66,641,2688 +163,641,2688 +22,642,2689 +22,643,2690 +130,643,2690 +22,644,2691 +57,644,2691 +71,644,2691 +173,644,2691 +22,645,2692 +60,645,2692 +151,645,2692 +22,646,2693 +75,646,2693 +212,646,2693 +22,647,2694 +22,648,2695 +122,648,2695 +22,649,2696 +100,649,2696 +22,650,2697 +121,650,2697 +163,650,2697 +22,651,2698 +135,651,2698 +22,652,2699 +22,653,2700 +47,653,2700 +182,653,2700 +22,654,2701 +49,654,2701 +22,655,2702 +77,655,2702 +22,656,2703 +22,657,2704 +67,657,2704 +160,657,2704 +22,658,2705 +59,658,2705 +23,659,2706 +93,659,2706 +125,659,2706 +23,660,2707 +130,660,2707 +23,661,2708 +48,661,2708 +145,661,2708 +23,662,2709 +66,662,2709 +23,663,2710 +193,663,2710 +23,664,2711 +45,664,2711 +23,665,2712 +23,666,2713 +67,666,2713 +23,667,2714 +23,668,2715 +23,669,2716 +23,670,2717 +70,670,2717 +23,671,2718 +68,671,2718 +141,671,2718 +23,672,2719 +37,672,2719 +23,673,2720 +56,673,2720 +23,674,2721 +149,674,2721 +23,675,2722 +23,676,2723 +72,676,2723 +23,677,2724 +128,677,2724 +23,678,2725 +23,679,2726 +68,679,2726 +175,679,2726 +23,680,2727 +23,681,2728 +149,681,2728 +23,682,2729 +149,682,2729 +23,683,2730 +87,683,2730 +23,684,2731 +23,685,2732 +56,685,2732 +149,685,2732 +23,686,2733 +128,686,2733 +23,687,2734 +66,687,2734 +23,688,2735 +136,688,2735 +24,689,2736 +112,689,2736 +24,690,2737 +37,690,2737 +147,690,2737 +24,691,2738 +67,691,2738 +141,691,2738 +24,692,2739 +100,692,2739 +171,692,2739 +190,692,2739 +24,693,2740 +24,694,2741 +78,694,2741 +178,694,2741 +24,695,2742 +141,695,2742 +24,696,2743 +57,696,2743 +189,696,2743 +206,696,2743 +24,697,2744 +76,697,2744 +24,698,2745 +224,698,2745 +24,699,2746 +34,699,2746 +24,700,2747 +112,700,2747 +24,701,2748 +24,702,2749 +24,703,2750 +144,703,2750 +24,704,2751 +142,704,2751 +24,705,2752 +24,706,2753 +50,706,2753 +24,707,2754 +51,707,2754 +152,707,2754 +24,708,2755 +137,708,2755 +24,709,2756 +57,709,2756 +69,709,2756 +24,710,2757 +24,711,2758 +57,711,2758 +24,712,2759 +57,712,2759 +24,713,2760 +87,713,2760 +170,713,2760 +24,714,2761 +116,714,2761 +24,715,2762 +72,715,2762 +24,716,2763 +69,716,2763 +24,717,2764 +81,717,2764 +198,717,2764 +24,718,2765 +130,718,2765 +25,719,2766 +130,719,2766 +171,719,2766 +25,720,2767 +138,720,2767 +25,721,2768 +51,721,2768 +173,721,2768 +25,722,2769 +25,723,2770 +25,724,2771 +79,724,2771 +25,725,2772 +181,725,2772 +25,726,2773 +25,727,2774 +57,727,2774 +25,728,2775 +74,728,2775 +134,728,2775 +25,729,2776 +25,730,2777 +57,730,2777 +71,730,2777 +25,731,2778 +104,731,2778 +25,732,2779 +36,732,2779 +25,733,2780 +43,733,2780 +221,733,2780 +25,734,2781 +25,735,2782 +210,735,2782 +25,736,2783 +37,736,2783 +57,736,2783 +153,736,2783 +25,737,2784 +42,737,2784 +64,737,2784 +25,738,2785 +166,738,2785 +25,739,2786 +84,739,2786 +225,739,2786 +25,740,2787 +113,740,2787 +183,740,2787 +25,741,2788 +25,742,2789 +114,742,2789 +178,742,2789 +25,743,2790 +181,743,2790 +232,743,2790 +25,744,2791 +38,744,2791 +59,744,2791 +25,745,2792 +81,745,2792 +25,746,2793 +102,746,2793 +25,747,2794 +100,747,2794 +25,748,2795 +98,748,2795 +139,748,2795 +26,749,2796 +132,749,2796 +26,750,2797 +26,751,2798 +26,752,2799 +89,752,2799 +26,753,2800 +134,753,2800 +26,754,2801 +125,754,2801 +26,755,2802 +26,756,2803 +51,756,2803 +137,756,2803 +26,757,2804 +45,757,2804 +157,757,2804 +26,758,2805 +43,758,2805 +126,758,2805 +26,759,2806 +104,759,2806 +165,759,2806 +26,760,2807 +122,760,2807 +26,761,2808 +26,762,2809 +132,762,2809 +222,762,2809 +26,763,2810 +51,763,2810 +56,763,2810 +26,764,2811 +50,764,2811 +140,764,2811 +26,765,2812 +26,766,2813 +26,767,2814 +48,767,2814 +26,768,2815 +150,768,2815 +26,769,2816 +173,769,2816 +26,770,2817 +127,770,2817 +26,771,2818 +96,771,2818 +26,772,2819 +113,772,2819 +26,773,2820 +76,773,2820 +191,773,2820 +26,774,2821 +26,775,2822 +42,775,2822 +26,776,2823 +62,776,2823 +26,777,2824 +26,778,2825 +63,778,2825 +27,779,2826 +85,779,2826 +134,779,2826 +27,780,2827 +37,780,2827 +58,780,2827 +27,781,2828 +98,781,2828 +27,782,2829 +27,783,2830 +27,784,2831 +170,784,2831 +27,785,2832 +57,785,2832 +27,786,2833 +150,786,2833 +205,786,2833 +27,787,2834 +38,787,2834 +27,788,2835 +60,788,2835 +204,788,2835 +27,789,2836 +125,789,2836 +27,790,2837 +51,790,2837 +27,791,2838 +93,791,2838 +126,791,2838 +27,792,2839 +49,792,2839 +57,792,2839 +167,792,2839 +27,793,2840 +27,794,2841 +46,794,2841 +27,795,2842 +57,795,2842 +65,795,2842 +136,795,2842 +27,796,2843 +125,796,2843 +27,797,2844 +27,798,2845 +86,798,2845 +27,799,2846 +85,799,2846 +27,800,2847 +115,800,2847 +184,800,2847 +229,800,2847 +27,801,2848 +100,801,2848 +27,802,2849 +84,802,2849 +27,803,2850 +83,803,2850 +27,804,2851 +49,804,2851 +190,804,2851 +27,805,2852 +111,805,2852 +174,805,2852 +27,806,2853 +96,806,2853 +27,807,2854 +27,808,2855 +59,808,2855 +158,808,2855 +28,809,2856 +28,810,2857 +28,811,2858 +28,812,2859 +152,812,2859 +191,812,2859 +208,812,2859 +28,813,2860 +85,813,2860 +28,814,2861 +28,815,2862 +79,815,2862 +28,816,2863 +28,817,2864 +130,817,2864 +28,818,2865 +28,819,2866 +135,819,2866 +28,820,2867 +148,820,2867 +28,821,2868 +28,822,2869 +83,822,2869 +28,823,2870 +213,823,2870 +28,824,2871 +149,824,2871 +28,825,2872 +44,825,2872 +161,825,2872 +28,826,2873 +88,826,2873 +28,827,2874 +28,828,2875 +105,828,2875 +28,829,2876 +28,830,2877 +66,830,2877 +28,831,2878 +28,832,2879 +37,832,2879 +57,832,2879 +28,833,2880 +40,833,2880 +178,833,2880 +28,834,2881 +28,835,2882 +28,836,2883 +28,837,2884 +28,838,2885 +29,839,2886 +101,839,2886 +29,840,2887 +29,841,2888 +64,841,2888 +199,841,2888 +29,842,2889 +199,842,2889 +29,843,2890 +37,843,2890 +175,843,2890 +29,844,2891 +63,844,2891 +141,844,2891 +29,845,2892 +29,846,2893 +62,846,2893 +215,846,2893 +29,847,2894 +29,848,2895 +57,848,2895 +70,848,2895 +136,848,2895 +29,849,2896 +227,849,2896 +29,850,2897 +150,850,2897 +29,851,2898 +29,852,2899 +29,853,2900 +29,854,2901 +49,854,2901 +29,855,2902 +150,855,2902 +29,856,2903 +29,857,2904 +74,857,2904 +186,857,2904 +29,858,2905 +146,858,2905 +167,858,2905 +29,859,2906 +46,859,2906 +138,859,2906 +29,860,2907 +84,860,2907 +29,861,2908 +149,861,2908 +29,862,2909 +29,863,2910 +125,863,2910 +29,864,2911 +140,864,2911 +192,864,2911 +29,865,2912 +74,865,2912 +29,866,2913 +84,866,2913 +29,867,2914 +64,867,2914 +166,867,2914 +29,868,2915 +82,868,2915 +136,868,2915 +30,869,2916 +81,869,2916 +30,870,2917 +85,870,2917 +195,870,2917 +30,871,2918 +100,871,2918 +149,871,2918 +201,871,2918 +30,872,2919 +64,872,2919 +176,872,2919 +30,873,2920 +93,873,2920 +30,874,2921 +133,874,2921 +30,875,2922 +30,876,2923 +30,877,2924 +34,877,2924 +181,877,2924 +30,878,2925 +30,879,2926 +59,879,2926 +232,879,2926 +30,880,2927 +194,880,2927 +30,881,2928 +148,881,2928 +30,882,2929 +46,882,2929 +30,883,2930 +30,884,2931 +30,885,2932 +37,885,2932 +133,885,2932 +30,886,2933 +127,886,2933 +30,887,2934 +30,888,2935 +45,888,2935 +30,889,2936 +74,889,2936 +184,889,2936 +30,890,2937 +48,890,2937 +203,890,2937 +30,891,2938 +56,891,2938 +148,891,2938 +30,892,2939 +110,892,2939 +30,893,2940 +42,893,2940 +59,893,2940 +30,894,2941 +30,895,2942 +30,896,2943 +140,896,2943 +30,897,2944 +152,897,2944 +30,898,2945 +131,898,2945 +31,899,2946 +40,899,2946 +59,899,2946 +31,900,2947 +31,901,2948 +148,901,2948 +31,902,2949 +67,902,2949 +123,902,2949 +31,903,2950 +86,903,2950 +156,903,2950 +31,904,2951 +31,905,2952 +101,905,2952 +31,906,2953 +50,906,2953 +135,906,2953 +31,907,2954 +150,907,2954 +31,908,2955 +133,908,2955 +31,909,2956 +108,909,2956 +139,909,2956 +31,910,2957 +44,910,2957 +31,911,2958 +31,912,2959 +101,912,2959 +145,912,2959 +31,913,2960 +44,913,2960 +161,913,2960 +31,914,2961 +140,914,2961 +31,915,2962 +122,915,2962 +31,916,2963 +106,916,2963 +31,917,2964 +36,917,2964 +131,917,2964 +31,918,2965 +131,918,2965 +31,919,2966 +31,920,2967 +31,921,2968 +105,921,2968 +31,922,2969 +78,922,2969 +168,922,2969 +31,923,2970 +42,923,2970 +31,924,2971 +138,924,2971 +31,925,2972 +122,925,2972 +31,926,2973 +31,927,2974 +74,927,2974 +31,928,2975 +215,928,2975 +32,929,2976 +32,930,2977 +91,930,2977 +32,931,2978 +32,932,2979 +215,932,2979 +32,933,2980 +32,934,2981 +32,935,2982 +43,935,2982 +32,936,2983 +131,936,2983 +32,937,2984 +184,937,2984 +32,938,2985 +102,938,2985 +177,938,2985 +32,939,2986 +98,939,2986 +149,939,2986 +200,939,2986 +32,940,2987 +50,940,2987 +32,941,2988 +116,941,2988 +32,942,2989 +182,942,2989 +32,943,2990 +32,944,2991 +181,944,2991 +32,945,2992 +89,945,2992 +179,945,2992 +32,946,2993 +32,947,2994 +32,948,2995 +32,949,2996 +193,949,2996 +32,950,2997 +32,951,2998 +47,951,2998 +164,951,2998 +32,952,2999 +32,953,3000 +131,953,3000 +32,954,3001 +32,955,3002 +46,955,3002 +173,955,3002 +32,956,3003 +32,957,3004 +151,957,3004 +32,958,3005 +78,958,3005 +33,959,3006 +67,959,3006 +33,960,3007 +61,960,3007 +134,960,3007 +33,961,3008 +60,961,3008 +33,962,3009 +60,962,3009 +129,962,3009 +225,962,3009 +33,963,3010 +33,964,3011 +85,964,3011 +33,965,3012 +43,965,3012 +175,965,3012 +33,966,3013 +74,966,3013 +226,966,3013 +33,967,3014 +46,967,3014 +127,967,3014 +33,968,3015 +50,968,3015 +33,969,3016 +130,969,3016 +33,970,3017 +68,970,3017 +169,970,3017 +218,970,3017 +33,971,3018 +69,971,3018 +33,972,3019 +83,972,3019 +33,973,3020 +83,973,3020 +158,973,3020 +33,974,3021 +80,974,3021 +229,974,3021 +33,975,3022 +69,975,3022 +33,976,3023 +127,976,3023 +33,977,3024 +80,977,3024 +154,977,3024 +33,978,3025 +59,978,3025 +151,978,3025 +192,978,3025 +33,979,3026 +61,979,3026 +203,979,3026 +33,980,3027 +180,980,3027 +196,980,3027 +207,980,3027 +33,981,3028 +63,981,3028 +133,981,3028 +33,982,3029 +80,982,3029 +33,983,3030 +57,983,3030 +71,983,3030 +33,984,3031 +48,984,3031 +140,984,3031 +33,985,3032 +33,986,3033 +50,986,3033 +133,986,3033 +33,987,3034 +45,987,3034 +155,987,3034 +33,988,3035 +34,989,3036 +129,989,3036 +34,990,3037 +34,991,3038 +63,991,3038 +34,992,3039 +205,992,3039 +34,993,3040 +34,994,3041 +57,994,3041 +117,994,3041 +34,995,3042 +132,995,3042 +205,995,3042 +34,996,3043 +34,997,3044 +100,997,3044 +34,998,3045 +65,998,3045 +161,998,3045 +34,999,3046 +137,999,3046 +34,1000,3047 +122,1000,3047 +161,1000,3047 +34,1001,3048 +159,1001,3048 +34,1002,3049 +68,1002,3049 +34,1003,3050 +34,1004,3051 +106,1004,3051 +34,1005,3052 +141,1005,3052 +34,1006,3053 +45,1006,3053 +34,1007,3054 +34,1008,3055 +134,1008,3055 +35,1009,3056 +64,1009,3056 +35,1010,3057 +112,1010,3057 +35,1011,3058 +35,1012,3059 +35,1013,3060 +198,1013,3060 +35,1014,3061 +35,1015,3062 +59,1015,3062 +147,1015,3062 +35,1016,3063 +135,1016,3063 +35,1017,3064 +94,1017,3064 +165,1017,3064 +35,1018,3065 +81,1018,3065 +35,1019,3066 +105,1019,3066 +233,1019,3066 +35,1020,3067 +35,1021,3068 +131,1021,3068 +35,1022,3069 +103,1022,3069 +35,1023,3070 +94,1023,3070 +35,1024,3071 +35,1025,3072 +206,1025,3072 +35,1026,3073 +80,1026,3073 +35,1027,3074 +205,1027,3074 +35,1028,3075 +224,1028,3075 +36,1029,3076 +195,1029,3076 +36,1030,3077 +79,1030,3077 +193,1030,3077 +36,1031,3078 +58,1031,3078 +36,1032,3079 +36,1033,3080 +36,1034,3081 +36,1035,3082 +162,1035,3082 +36,1036,3083 +36,1037,3084 +36,1038,3085 +36,1039,3086 +36,1040,3087 +138,1040,3087 +36,1041,3088 +129,1041,3088 +36,1042,3089 +36,1043,3090 +140,1043,3090 +36,1044,3091 +36,1045,3092 +118,1045,3092 +36,1046,3093 +69,1046,3093 +36,1047,3094 +191,1047,3094 +36,1048,3095 +99,1048,3095 +37,1049,3096 +140,1049,3096 +37,1050,3097 +119,1050,3097 +37,1051,3098 +75,1051,3098 +37,1052,3099 +81,1052,3099 +37,1053,3100 +172,1053,3100 +37,1054,3101 +131,1054,3101 +216,1054,3101 +37,1055,3102 +47,1055,3102 +212,1055,3102 +37,1056,3103 +37,1057,3104 +90,1057,3104 +182,1057,3104 +233,1057,3104 +37,1058,3105 +37,1059,3106 +89,1059,3106 +135,1059,3106 +37,1060,3107 +37,1061,3108 +172,1061,3108 +37,1062,3109 +158,1062,3109 +37,1063,3110 +131,1063,3110 +37,1064,3111 +37,1065,3112 +114,1065,3112 +37,1066,3113 +186,1066,3113 +218,1066,3113 +37,1067,3114 +111,1067,3114 +183,1067,3114 +37,1068,3115 +73,1068,3115 +38,1069,3116 +118,1069,3116 +127,1069,3116 +38,1070,3117 +38,1071,3118 +170,1071,3118 +38,1072,3119 +81,1072,3119 +38,1073,3120 +131,1073,3120 +38,1074,3121 +67,1074,3121 +38,1075,3122 +104,1075,3122 +38,1076,3123 +38,1077,3124 +38,1078,3125 +121,1078,3125 +175,1078,3125 +38,1079,3126 +57,1079,3126 +65,1079,3126 +138,1079,3126 +38,1080,3127 +38,1081,3128 +108,1081,3128 +38,1082,3129 +38,1083,3130 +75,1083,3130 +38,1084,3131 +172,1084,3131 +38,1085,3132 +57,1085,3132 +77,1085,3132 +38,1086,3133 +75,1086,3133 +219,1086,3133 +38,1087,3134 +38,1088,3135 +155,1088,3135 +39,1089,3136 +39,1090,3137 +39,1091,3138 +114,1091,3138 +39,1092,3139 +75,1092,3139 +39,1093,3140 +61,1093,3140 +39,1094,3141 +39,1095,3142 +102,1095,3142 +39,1096,3143 +69,1096,3143 +180,1096,3143 +39,1097,3144 +176,1097,3144 +211,1097,3144 +39,1098,3145 +92,1098,3145 +39,1099,3146 +187,1099,3146 +39,1100,3147 +148,1100,3147 +39,1101,3148 +39,1102,3149 +105,1102,3149 +39,1103,3150 +75,1103,3150 +182,1103,3150 +39,1104,3151 +74,1104,3151 +39,1105,3152 +39,1106,3153 +73,1106,3153 +39,1107,3154 +39,1108,3155 +76,1108,3155 +40,1109,3156 +123,1109,3156 +179,1109,3156 +40,1110,3157 +40,1111,3158 +87,1111,3158 +40,1112,3159 +62,1112,3159 +40,1113,3160 +73,1113,3160 +40,1114,3161 +94,1114,3161 +174,1114,3161 +40,1115,3162 +73,1115,3162 +93,1115,3162 +112,1115,3162 +40,1116,3163 +40,1117,3164 +79,1117,3164 +138,1117,3164 +40,1118,3165 +171,1118,3165 +208,1118,3165 +40,1119,3166 +60,1119,3166 +184,1119,3166 +40,1120,3167 +214,1120,3167 +40,1121,3168 +40,1122,3169 +70,1122,3169 +132,1122,3169 +40,1123,3170 +40,1124,3171 +133,1124,3171 +185,1124,3171 +40,1125,3172 +94,1125,3172 +229,1125,3172 +40,1126,3173 +99,1126,3173 +220,1126,3173 +40,1127,3174 +108,1127,3174 +40,1128,3175 +76,1128,3175 +41,1129,3176 +110,1129,3176 +158,1129,3176 +41,1130,3177 +137,1130,3177 +204,1130,3177 +41,1131,3178 +41,1132,3179 +129,1132,3179 +41,1133,3180 +97,1133,3180 +178,1133,3180 +41,1134,2686 +108,1134,2686 +123,1134,2686 +41,1135,3181 +180,1135,3181 +41,1136,3182 +63,1136,3182 +41,1137,3183 +41,1138,3184 +57,1138,3184 +133,1138,3184 +41,1139,3185 +62,1139,3185 +41,1140,3186 +106,1140,3186 +41,1141,3187 +131,1141,3187 +224,1141,3187 +41,1142,3188 +73,1142,3188 +41,1143,3189 +80,1143,3189 +41,1144,3190 +174,1144,3190 +41,1145,3191 +41,1146,3192 +203,1146,3192 +41,1147,3193 +41,1148,3194 +164,1148,3194 +42,1149,3195 +75,1149,3195 +42,1150,3196 +42,1151,3197 +170,1151,3197 +231,1151,3197 +42,1152,3198 +142,1152,3198 +42,1153,3199 +42,1154,3200 +88,1154,3200 +42,1155,3201 +42,1156,3202 +172,1156,3202 +42,1157,3203 +86,1157,3203 +137,1157,3203 +42,1158,3204 +69,1158,3204 +42,1159,3205 +42,1160,3206 +42,1161,3207 +172,1161,3207 +42,1162,3208 +123,1162,3208 +171,1162,3208 +42,1163,3209 +42,1164,3210 +120,1164,3210 +42,1165,3211 +215,1165,3211 +42,1166,3212 +80,1166,3212 +42,1167,3213 +84,1167,3213 +42,1168,3214 +82,1168,3214 +228,1168,3214 +43,1169,3215 +96,1169,3215 +43,1170,3216 +126,1170,3216 +43,1171,3217 +178,1171,3217 +43,1172,3218 +151,1172,3218 +43,1173,3219 +72,1173,3219 +43,1174,3220 +179,1174,3220 +43,1175,3221 +187,1175,3221 +219,1175,3221 +43,1176,3222 +153,1176,3222 +43,1177,3223 +96,1177,3223 +43,1178,3224 +104,1178,3224 +221,1178,3224 +43,1179,3225 +43,1180,3226 +43,1181,3227 +146,1181,3227 +43,1182,3228 +76,1182,3228 +43,1183,3229 +119,1183,3229 +43,1184,3230 +160,1184,3230 +43,1185,3231 +43,1186,3232 +43,1187,2945 +43,1188,3233 +44,1189,3234 +82,1189,3234 +44,1190,3235 +71,1190,3235 +125,1190,3235 +44,1191,3236 +44,1192,3237 +168,1192,3237 +44,1193,3238 +135,1193,3238 +44,1194,3239 +44,1195,3240 +197,1195,3240 +44,1196,3241 +57,1196,3241 +77,1196,3241 +44,1197,3242 +44,1198,3243 +92,1198,3243 +44,1199,3244 +126,1199,3244 +44,1200,3245 +65,1200,3245 +44,1201,3246 +185,1201,3246 +44,1202,3247 +94,1202,3247 +140,1202,3247 +44,1203,3248 +44,1204,3249 +44,1205,3250 +171,1205,3250 +44,1206,3251 +166,1206,3251 +44,1207,2533 +44,1208,3252 +154,1208,3252 +185,1208,3252 +45,1209,3253 +119,1209,3253 +45,1210,3254 +45,1211,3255 +140,1211,3255 +219,1211,3255 +45,1212,3256 +68,1212,3256 +134,1212,3256 +45,1213,3257 +45,1214,3258 +67,1214,3258 +161,1214,3258 +45,1215,3259 +225,1215,3259 +45,1216,3260 +116,1216,3260 +45,1217,3261 +45,1218,3262 +107,1218,3262 +45,1219,3263 +45,1220,3264 +77,1220,3264 +45,1221,3265 +106,1221,3265 +45,1222,3266 +57,1222,3266 +151,1222,3266 +200,1222,3266 +45,1223,3267 +137,1223,3267 +45,1224,3268 +45,1225,3269 +94,1225,3269 +153,1225,3269 +45,1226,3270 +67,1226,3270 +45,1227,3271 +69,1227,3271 +162,1227,3271 +45,1228,3272 +46,1229,3273 +68,1229,3273 +46,1230,3274 +46,1231,3275 +46,1232,3276 +56,1232,3276 +150,1232,3276 +46,1233,3277 +230,1233,3277 +46,1234,3278 +175,1234,3278 +46,1235,3279 +197,1235,3279 +207,1235,3279 +46,1236,3280 +56,1236,3280 +46,1237,3281 +153,1237,3281 +46,1238,3282 +105,1238,3282 +46,1239,3283 +56,1239,3283 +46,1240,3284 +177,1240,3284 +46,1241,3285 +97,1241,3285 +46,1242,3286 +56,1242,3286 +46,1243,3287 +127,1243,3287 +46,1244,3288 +61,1244,3288 +46,1245,3289 +132,1245,3289 +46,1246,3290 +205,1246,3290 +46,1247,3291 +84,1247,3291 +166,1247,3291 +46,1248,3292 +150,1248,3292 +47,1249,3293 +76,1249,3293 +47,1250,3294 +115,1250,3294 +47,1251,3295 +47,1252,3296 +170,1252,3296 +47,1253,3297 +56,1253,3297 +153,1253,3297 +47,1254,3298 +177,1254,3298 +47,1255,3299 +47,1256,3300 +56,1256,3300 +47,1257,3301 +47,1258,3302 +47,1259,3303 +117,1259,3303 +47,1260,3304 +170,1260,3304 +47,1261,3305 +148,1261,3305 +164,1261,3305 +47,1262,3306 +172,1262,3306 +47,1263,3307 +57,1263,3307 +210,1263,3307 +47,1264,3308 +47,1265,3309 +56,1265,3309 +103,1265,3309 +47,1266,3310 +101,1266,3310 +47,1267,3311 +116,1267,3311 +47,1268,3312 +159,1268,3312 +48,1269,3313 +48,1270,3314 +103,1270,3314 +176,1270,3314 +48,1271,3315 +176,1271,3315 +48,1272,3316 +97,1272,3316 +123,1272,3316 +48,1273,3317 +75,1273,3317 +48,1274,3318 +48,1275,3319 +48,1276,3320 +56,1276,3320 +48,1277,3321 +48,1278,3322 +48,1279,3323 +139,1279,3323 +48,1280,3324 +99,1280,3324 +48,1281,3325 +84,1281,3325 +48,1282,3326 +56,1282,3326 +48,1283,3327 +87,1283,3327 +48,1284,3328 +128,1284,3328 +48,1285,3329 +56,1285,3329 +48,1286,3330 +90,1286,3330 +48,1287,3331 +133,1287,3331 +159,1287,3331 +48,1288,3332 +73,1288,3332 +49,1289,3333 +85,1289,3333 +163,1289,3333 +49,1290,3334 +49,1291,3335 +86,1291,3335 +202,1291,3335 +49,1292,3336 +107,1292,3336 +185,1292,3336 +49,1293,3337 +68,1293,3337 +49,1294,3338 +132,1294,3338 +49,1295,3339 +82,1295,3339 +49,1296,3340 +93,1296,3340 +49,1297,3341 +206,1297,3341 +49,1298,3342 +57,1298,3342 +70,1298,3342 +155,1298,3342 +49,1299,3343 +111,1299,3343 +49,1300,3344 +98,1300,3344 +49,1301,3345 +49,1302,3346 +81,1302,3346 +49,1303,3347 +49,1304,3348 +141,1304,3348 +49,1305,3349 +121,1305,3349 +49,1306,3350 +151,1306,3350 +232,1306,3350 +49,1307,3351 +49,1308,3352 +146,1308,3352 +186,1308,3352 +50,1309,3353 +50,1310,3354 +50,1311,3355 +102,1311,3355 +50,1312,3356 +153,1312,3356 +50,1313,3357 +82,1313,3357 +50,1314,3358 +50,1315,3359 +102,1315,3359 +50,1316,3360 +151,1316,3360 +50,1317,3361 +97,1317,3361 +50,1318,3362 +50,1319,3363 +50,1320,3364 +50,1321,3365 +56,1321,3365 +50,1322,3366 +146,1322,3366 +50,1323,3367 +130,1323,3367 +50,1324,3368 +50,1325,3369 +83,1325,3369 +50,1326,3370 +148,1326,3370 +50,1327,3371 +165,1327,3371 +50,1328,3372 +85,1328,3372 +51,1329,3373 +74,1329,3373 +150,1329,3373 +51,1330,3374 +51,1331,3375 +127,1331,3375 +51,1332,3376 +76,1332,3376 +51,1333,3377 +86,1333,3377 +51,1334,3378 +74,1334,3378 +214,1334,3378 +51,1335,3379 +127,1335,3379 +213,1335,3379 +51,1336,3380 +142,1336,3380 +51,1337,3381 +51,1338,3382 +71,1338,3382 +51,1339,3383 +103,1339,3383 +146,1339,3383 +51,1340,3384 +51,1341,3385 +51,1342,3386 +51,1343,3387 +57,1343,3387 +51,1344,3388 +88,1344,3388 +155,1344,3388 +51,1345,3389 +78,1345,3389 +51,1346,3390 +208,1346,3390 +231,1346,3390 +51,1347,3391 +51,1348,3392 +58,3393,3394 +111,3393,3394 +155,3393,3394 +58,3395,3396 +136,3395,3396 +154,3395,3396 +218,3395,3396 +58,3397,3398 +58,3399,3400 +123,3399,3400 +58,3401,3402 +142,3401,3402 +58,3403,3404 +136,3403,3404 +58,3405,3406 +58,3407,3408 +58,3409,3410 +58,3411,3412 +144,3411,3412 +230,3411,3412 +58,3413,3414 +186,3413,3414 +58,3415,3416 +126,3415,3416 +58,3417,3418 +56,3419,3420 +58,3419,3420 +158,3419,3420 +58,3421,3422 +137,3421,3422 +172,3421,3422 +58,3423,3424 +58,3425,3426 +92,3425,3426 +58,3427,3428 +75,3427,3428 +158,3427,3428 +58,3429,3430 +58,3431,3432 +59,3433,3434 +101,3433,3434 +59,3435,3436 +139,3435,3436 +59,3437,3438 +103,3437,3438 +59,3439,3440 +179,3439,3440 +59,3441,3442 +59,3443,3444 +59,3445,3446 +160,3445,3446 +59,3447,3448 +59,3449,3450 +59,3451,3452 +59,3453,3454 +59,3455,3456 +59,3457,3458 +163,3457,3458 +59,3459,3460 +91,3459,3460 +59,3461,3462 +117,3461,3462 +59,3463,3464 +59,3465,3466 +158,3465,3466 +59,3467,3468 +59,3469,3470 +88,3469,3470 +59,3471,3472 +133,3471,3472 +60,3473,3474 +196,3473,3474 +200,3473,3474 +60,3475,3476 +60,3477,3478 +126,3477,3478 +60,3479,3480 +89,3479,3480 +60,3481,3482 +60,3483,3484 +73,3483,3484 +162,3483,3484 +60,3485,3486 +60,3487,3488 +83,3487,3488 +169,3487,3488 +60,3489,3490 +60,3491,3492 +151,3491,3492 +60,3493,3494 +93,3493,3494 +60,3495,3496 +77,3495,3496 +156,3495,3496 +60,3497,3498 +60,3499,3500 +97,3499,3500 +60,3501,3502 +60,3503,3504 +91,3503,3504 +60,3505,3506 +180,3505,3506 +60,3507,3508 +78,3507,3508 +60,3509,3510 +147,3509,3510 +60,3511,3512 +158,3511,3512 +61,3513,3514 +61,3515,3516 +61,3517,3518 +61,3519,3520 +127,3519,3520 +151,3519,3520 +61,3521,3522 +61,3523,3524 +61,3525,3526 +153,3525,3526 +61,3527,3528 +61,3529,3530 +91,3529,3530 +130,3529,3530 +227,3529,3530 +57,3531,3532 +61,3531,3532 +56,3533,3534 +61,3533,3534 +61,3535,3536 +181,3535,3536 +61,3537,3538 +186,3537,3538 +57,3539,3540 +61,3539,3540 +161,3539,3540 +56,3541,3542 +61,3541,3542 +61,3543,3544 +86,3543,3544 +61,3545,3546 +186,3545,3546 +61,3547,3548 +128,3547,3548 +56,3549,3550 +61,3549,3550 +61,3551,3552 +62,3553,3554 +144,3553,3554 +172,3553,3554 +62,3555,3556 +62,3557,3558 +62,3559,3560 +150,3559,3560 +62,3561,3562 +62,3563,3564 +185,3563,3564 +226,3563,3564 +62,3565,3566 +99,3565,3566 +154,3565,3566 +62,3567,3568 +177,3567,3568 +62,3569,3570 +62,3571,3572 +142,3571,3572 +224,3571,3572 +62,3573,3574 +62,3575,3576 +136,3575,3576 +182,3575,3576 +62,3577,3578 +62,3579,3580 +62,3581,3582 +220,3581,3582 +62,3583,3584 +112,3583,3584 +219,3583,3584 +62,3585,3586 +169,3585,3586 +57,3587,3588 +62,3587,3588 +71,3587,3588 +170,3587,3588 +190,3587,3588 +62,3589,3590 +62,3591,3592 +155,3591,3592 +63,3593,3594 +123,3593,3594 +63,3595,3596 +110,3595,3596 +63,3597,3598 +63,3599,3600 +84,3599,3600 +63,3601,3602 +79,3601,3602 +63,3603,3604 +161,3603,3604 +63,3605,3606 +63,3607,3608 +63,3609,3610 +85,3609,3610 +173,3609,3610 +63,3611,3612 +63,3613,3614 +183,3613,3614 +63,3615,3616 +63,3617,3618 +63,3619,3620 +63,3621,3622 +230,3621,3622 +63,3623,3624 +63,3625,3626 +165,3625,3626 +63,3627,3628 +93,3627,3628 +146,3627,3628 +63,3629,3630 +63,3631,3632 +161,3631,3632 +64,3633,3634 +77,3633,3634 +210,3633,3634 +64,3635,3636 +174,3635,3636 +64,3637,3638 +156,3637,3638 +64,3639,3640 +103,3639,3640 +64,3641,3642 +173,3641,3642 +64,3643,3644 +80,3643,3644 +174,3643,3644 +64,3645,3646 +170,3645,3646 +64,3647,3648 +64,3649,3650 +64,3651,3652 +64,3653,3654 +153,3653,3654 +64,3655,3656 +64,3657,3658 +146,3657,3658 +64,3659,3660 +137,3659,3660 +64,3661,3662 +82,3661,3662 +64,3663,3664 +158,3663,3664 +64,3665,3666 +64,3667,3668 +118,3667,3668 +64,3669,3670 +126,3669,3670 +64,3671,3672 +121,3671,3672 +57,3673,3674 +65,3673,3674 +152,3673,3674 +65,3675,3676 +112,3675,3676 +146,3675,3676 +210,3675,3676 +65,3677,3678 +65,3679,3680 +169,3679,3680 +65,3681,3682 +226,3681,3682 +65,3683,3684 +110,3683,3684 +141,3683,3684 +65,3685,3686 +132,3685,3686 +174,3685,3686 +65,3687,3688 +117,3687,3688 +65,3689,3690 +171,3689,3690 +65,3691,3692 +225,3691,3692 +65,3693,3694 +65,3695,3696 +107,3695,3696 +139,3695,3696 +65,3697,3698 +65,3699,3700 +175,3699,3700 +65,3701,3702 +65,3703,3704 +65,3705,3706 +185,3705,3706 +65,3707,3708 +125,3707,3708 +172,3707,3708 +65,3709,3710 +113,3709,3710 +65,3711,3712 +161,3711,3712 +66,3713,3714 +66,3715,3716 +66,3717,3718 +66,3719,3720 +144,3719,3720 +66,3721,3722 +66,3723,3724 +100,3723,3724 +165,3723,3724 +66,3725,3726 +191,3725,3726 +66,3727,3728 +97,3727,3728 +66,3729,3730 +109,3729,3730 +66,3731,3732 +77,3731,3732 +171,3731,3732 +221,3731,3732 +66,3733,3734 +88,3733,3734 +146,3733,3734 +66,3735,3736 +156,3735,3736 +66,3737,3738 +87,3737,3738 +66,3739,3740 +118,3739,3740 +66,3741,3742 +99,3741,3742 +140,3741,3742 +66,3743,3744 +66,3745,3746 +183,3745,3746 +66,3747,3748 +229,3747,3748 +66,3749,3750 +66,3751,3752 +67,3753,3754 +119,3753,3754 +67,3755,3756 +216,3755,3756 +67,3757,3758 +151,3757,3758 +67,3759,3760 +161,3759,3760 +67,3761,3762 +67,3763,3764 +67,3765,3766 +89,3765,3766 +57,3767,3768 +67,3767,3768 +104,3767,3768 +67,3769,3770 +67,3771,3772 +67,3773,3774 +67,3775,3776 +67,3777,3778 +67,3779,3780 +220,3779,3780 +67,3781,3782 +79,3781,3782 +67,3783,3784 +67,3785,3786 +67,3787,3788 +67,3789,3790 +67,3791,3792 +97,3791,3792 +166,3791,3792 +68,3793,3794 +68,3795,3796 +160,3795,3796 +68,3797,3798 +161,3797,3798 +229,3797,3798 +68,3799,3800 +68,3801,3802 +68,3803,3804 +176,3803,3804 +68,3805,3806 +68,3807,3808 +115,3807,3808 +68,3809,3810 +93,3809,3810 +68,3811,3812 +210,3811,3812 +68,3813,3814 +68,3815,3816 +166,3815,3816 +68,3817,3818 +135,3817,3818 +216,3817,3818 +68,3819,3820 +120,3819,3820 +193,3819,3820 +68,3821,3822 +68,3823,3824 +68,3825,3826 +89,3825,3826 +68,3827,3828 +120,3827,3828 +186,3827,3828 +68,3829,3830 +177,3829,3830 +68,3831,3832 +165,3831,3832 +69,3833,3834 +69,3835,3836 +108,3835,3836 +69,3837,3838 +111,3837,3838 +69,3839,3840 +69,3841,3842 +111,3841,3842 +69,3843,3844 +92,3843,3844 +56,3845,3846 +69,3845,3846 +109,3845,3846 +69,3847,3848 +69,3849,3850 +110,3849,3850 +165,3849,3850 +69,3851,3852 +86,3851,3852 +69,3853,3854 +187,3853,3854 +69,3855,3856 +69,3857,3858 +154,3857,3858 +69,3859,3860 +69,3861,3862 +88,3861,3862 +165,3861,3862 +223,3861,3862 +69,3863,3864 +114,3863,3864 +69,3865,3866 +69,3867,3868 +69,3869,3870 +69,3871,3872 +70,3873,3874 +146,3873,3874 +70,3875,3876 +70,3877,3878 +70,3879,3880 +57,3881,3882 +70,3881,3882 +200,3881,3882 +70,3883,3884 +70,3885,3886 +70,3887,3888 +70,3889,3890 +70,3891,3892 +70,3893,3894 +121,3893,3894 +70,3895,3896 +92,3895,3896 +228,3895,3896 +70,3897,3898 +70,3899,3900 +92,3899,3900 +70,3901,3902 +103,3901,3902 +70,3903,3904 +152,3903,3904 +163,3903,3904 +57,3905,3906 +70,3905,3906 +190,3905,3906 +70,3907,3908 +70,3909,3910 +89,3909,3910 +70,3911,3912 +71,3913,3914 +84,3913,3914 +156,3913,3914 +71,3915,3916 +116,3915,3916 +71,3917,3918 +71,3919,3920 +141,3919,3920 +71,3921,3922 +181,3921,3922 +208,3921,3922 +71,3923,3924 +93,3923,3924 +140,3923,3924 +71,3925,3926 +71,3927,3928 +123,3927,3928 +71,3929,3930 +154,3929,3930 +192,3929,3930 +71,3931,3932 +71,3933,3934 +71,3935,3936 +71,3937,3938 +191,3937,3938 +71,3939,3940 +71,3941,3942 +71,3943,3944 +196,3943,3944 +217,3943,3944 +71,3945,3946 +139,3945,3946 +71,3947,3948 +71,3949,3950 +109,3949,3950 +167,3949,3950 +71,3951,3952 +72,3953,3954 +97,3953,3954 +72,3955,3956 +137,3955,3956 +72,3957,3958 +162,3957,3958 +72,3959,3960 +163,3959,3960 +72,3961,3962 +113,3961,3962 +72,3963,3964 +131,3963,3964 +72,3965,3966 +99,3965,3966 +72,3967,3968 +111,3967,3968 +72,3969,3970 +97,3969,3970 +72,3971,3972 +98,3971,3972 +72,3973,3974 +201,3973,3974 +72,3975,3976 +145,3975,3976 +72,3977,3978 +101,3977,3978 +164,3977,3978 +72,3979,3980 +91,3979,3980 +72,3981,3982 +163,3981,3982 +72,3983,3984 +72,3985,3986 +135,3985,3986 +72,3987,3988 +103,3987,3988 +72,3989,3990 +72,3991,3992 +104,3991,3992 +195,3991,3992 +73,3993,3994 +73,3995,3996 +186,3995,3996 +73,3997,3998 +89,3997,3998 +73,3999,4000 +172,3999,4000 +73,4001,4002 +161,4001,4002 +73,4003,4004 +73,4007,4008 +109,4007,4008 +73,4009,4010 +152,4009,4010 +73,4011,4012 +134,4011,4012 +73,4013,4014 +129,4013,4014 +184,4013,4014 +73,4015,4016 +73,4017,4018 +147,4017,4018 +73,4019,4020 +128,4019,4020 +73,4021,4022 +85,4021,4022 +73,4023,4024 +73,4025,4026 +145,4025,4026 +73,4027,4028 +73,4029,4030 +73,4031,4032 +74,4033,4034 +123,4033,4034 +213,4033,4034 +74,4035,4036 +152,4035,4036 +74,4037,4038 +74,4039,4040 +145,4039,4040 +74,4041,4042 +157,4041,4042 +74,4043,4044 +93,4043,4044 +180,4043,4044 +74,4045,4046 +74,4047,4048 +74,4049,4050 +134,4049,4050 +74,4051,4052 +74,4053,4054 +110,4053,4054 +74,4055,4056 +74,4057,4058 +74,4059,4060 +74,4061,4062 +74,4063,4064 +85,4063,4064 +231,4063,4064 +74,4065,4066 +74,4067,4068 +74,4069,4070 +74,4071,4072 +75,4073,4074 +182,4073,4074 +75,4075,4076 +126,4075,4076 +75,4077,4078 +170,4077,4078 +75,4079,4080 +149,4079,4080 +75,4081,4082 +75,4083,4084 +75,4085,4086 +206,4085,4086 +75,4087,4088 +75,4089,4090 +86,4089,4090 +75,4091,4092 +132,4091,4092 +75,4093,4094 +96,4093,4094 +75,4095,4096 +144,4095,4096 +75,4097,4098 +86,4097,4098 +75,4099,4100 +75,4101,4102 +130,4101,4102 +75,4103,4104 +75,4105,4106 +130,4105,4106 +75,4107,4108 +126,4107,4108 +75,4109,4110 +166,4109,4110 +75,4111,4112 +90,4111,4112 +170,4111,4112 +76,4113,4114 +87,4113,4114 +179,4113,4114 +76,4115,4116 +76,4117,4118 +76,4119,4120 +98,4119,4120 +155,4119,4120 +76,4121,4122 +102,4121,4122 +233,4121,4122 +76,4123,4124 +129,4123,4124 +211,4123,4124 +76,4125,4126 +127,4125,4126 +76,4127,4128 +106,4127,4128 +76,4129,4130 +76,4131,4132 +156,4131,4132 +76,4133,4134 +76,4135,4136 +161,4135,4136 +76,4137,4138 +216,4137,4138 +76,4139,4140 +115,4139,4140 +76,4141,4142 +106,4141,4142 +76,4143,4144 +116,4143,4144 +76,4145,4146 +109,4145,4146 +76,4147,4148 +94,4147,4148 +76,4149,4150 +138,4149,4150 +76,4151,4152 +126,4151,4152 +167,4151,4152 +77,4153,4154 +187,4153,4154 +77,4155,4156 +88,4155,4156 +176,4155,4156 +77,4157,4158 +77,4159,4160 +109,4159,4160 +219,4159,4160 +77,4161,4162 +156,4161,4162 +216,4161,4162 +77,4163,4164 +163,4163,4164 +77,4165,4166 +141,4165,4166 +77,4167,4168 +100,4167,4168 +77,4169,4170 +185,4169,4170 +77,4171,4172 +91,4171,4172 +169,4171,4172 +77,4173,4174 +77,4175,4176 +77,4177,4178 +216,4177,4178 +77,4179,4180 +106,4179,4180 +77,4181,4182 +178,4181,4182 +77,4183,4184 +77,4185,4186 +77,4187,4188 +77,4189,4190 +107,4189,4190 +77,4191,4192 +78,4193,4194 +101,4193,4194 +78,4195,4196 +78,4197,4198 +115,4197,4198 +78,4199,4200 +78,4201,4202 +100,4201,4202 +78,4203,4204 +94,4203,4204 +78,4205,4206 +78,4207,4208 +184,4207,4208 +78,4209,4210 +78,4211,4212 +78,4213,4214 +78,4215,4216 +88,4215,4216 +179,4215,4216 +78,4217,4218 +78,4219,4220 +78,4221,4222 +78,4223,4224 +78,4225,4226 +78,4227,4228 +200,4227,4228 +78,4229,4230 +140,4229,4230 +78,4231,4232 +159,4231,4232 +79,4233,4234 +79,4235,4236 +162,4235,4236 +79,4237,4238 +123,4237,4238 +135,4237,4238 +79,4239,4240 +79,4241,4242 +105,4241,4242 +79,4243,4244 +79,4245,4246 +173,4245,4246 +209,4245,4246 +79,4247,4248 +120,4247,4248 +217,4247,4248 +79,4249,4250 +118,4249,4250 +79,4251,4252 +79,4253,4254 +79,4255,4256 +79,4257,4258 +79,4259,4260 +99,4259,4260 +137,4259,4260 +79,4261,4262 +102,4261,4262 +79,4263,4264 +116,4263,4264 +79,4265,4266 +79,4267,4268 +88,4267,4268 +79,4269,4270 +134,4269,4270 +79,4271,4272 +120,4271,4272 +80,4273,4274 +80,4275,4276 +80,4277,4278 +80,4279,4280 +80,4281,4282 +80,4283,4284 +158,4283,4284 +80,4285,4286 +160,4285,4286 +80,4287,4288 +154,4287,4288 +80,4289,4290 +114,4289,4290 +80,4291,4292 +80,4293,4294 +80,4295,4296 +129,4295,4296 +80,4297,4298 +113,4297,4298 +80,4299,4300 +80,4301,4302 +80,4303,4304 +80,4305,4306 +183,4305,4306 +80,4307,4308 +80,4309,4310 +147,4309,4310 +229,4309,4310 +80,4311,4312 +81,4313,4314 +119,4313,4314 +81,4315,4316 +81,4319,4320 +81,4321,4322 +81,4323,4324 +81,4325,4326 +119,4325,4326 +81,4327,4328 +168,4327,4328 +81,4329,4330 +190,4329,4330 +81,4333,4334 +81,4335,4336 +168,4335,4336 +81,4337,4338 +81,4339,4340 +81,4341,4342 +81,4343,4344 +157,4343,4344 +81,4345,4346 +81,4347,4348 +166,4347,4348 +81,4349,4350 +81,4351,4352 +82,4353,4354 +138,4353,4354 +82,4355,4356 +99,4355,4356 +82,4357,4358 +99,4357,4358 +157,4357,4358 +82,4359,4360 +162,4359,4360 +202,4359,4360 +82,4361,4362 +99,4361,4362 +82,4363,4364 +155,4363,4364 +82,4365,4366 +182,4365,4366 +82,4367,4368 +82,4369,4370 +82,4371,4372 +82,4373,4374 +104,4373,4374 +82,4375,4376 +82,4377,4378 +82,4379,4380 +103,4379,4380 +82,4381,4382 +82,4383,4384 +82,4385,4386 +135,4385,4386 +151,4385,4386 +82,4387,4388 +82,4389,4390 +123,4389,4390 +231,4389,4390 +82,4391,4392 +167,4391,4392 +83,4393,4394 +83,4395,4396 +83,4397,4398 +83,4399,4400 +83,4401,4402 +83,4403,4404 +186,4403,4404 +83,4405,4406 +108,4405,4406 +182,4405,4406 +217,4405,4406 +83,4407,4408 +83,4409,4410 +83,4411,4412 +83,4413,4414 +83,4415,4416 +83,4417,4418 +155,4417,4418 +217,4417,4418 +83,4419,4420 +83,4421,4422 +221,4421,4422 +83,4423,4424 +83,4425,4426 +83,4427,4428 +120,4427,4428 +83,4429,4430 +83,4431,4432 +169,4431,4432 +84,4433,4434 +84,4435,4436 +175,4435,4436 +84,4437,4438 +115,4437,4438 +84,4439,4440 +169,4439,4440 +84,4441,4442 +104,4441,4442 +84,4443,4444 +84,4445,4446 +84,4447,4448 +120,4447,4448 +201,4447,4448 +84,4449,4450 +84,4451,4452 +145,4451,4452 +84,4453,4454 +84,4455,4456 +205,4455,4456 +84,4457,4458 +84,4459,4460 +84,4461,4462 +147,4461,4462 +84,4463,4464 +101,4463,4464 +211,4463,4464 +84,4465,4466 +84,4467,4468 +84,4469,4470 +98,4469,4470 +217,4469,4470 +84,4471,4472 +85,4473,4474 +125,4473,4474 +142,4473,4474 +85,4475,4476 +85,4477,4478 +85,4479,4480 +171,4479,4480 +85,4481,4482 +112,4481,4482 +85,4483,4484 +85,4485,4486 +85,4487,4488 +114,4487,4488 +85,4489,4490 +85,4491,4492 +85,4493,4494 +195,4493,4494 +196,4493,4494 +85,4495,4496 +85,4497,4498 +110,4497,4498 +182,4497,4498 +85,4499,4500 +85,4501,4502 +85,4503,4504 +209,4503,4504 +222,4503,4504 +85,4505,4506 +85,4507,4508 +85,4509,4510 +85,4511,4512 +185,4511,4512 +86,4513,4514 +119,4513,4514 +86,4515,4516 +86,4517,4518 +132,4517,4518 +145,4517,4518 +86,4519,4520 +165,4519,4520 +86,4521,4522 +86,4523,4524 +163,4523,4524 +202,4523,4524 +86,4525,4526 +185,4525,4526 +86,4527,4528 +102,4527,4528 +86,4529,4530 +86,4531,4532 +86,4533,4534 +114,4533,4534 +86,4535,4536 +86,4537,4538 +113,4537,4538 +148,4537,4538 +86,4539,4540 +149,4539,4540 +179,4539,4540 +86,4541,4542 +156,4541,4542 +86,4543,4544 +138,4543,4544 +86,4545,4546 +120,4545,4546 +209,4545,4546 +86,4547,4548 +142,4547,4548 +86,4549,4550 +86,4551,4552 +104,4551,4552 +87,4553,4554 +120,4553,4554 +87,4555,4556 +108,4555,4556 +167,4555,4556 +87,4557,4558 +164,4557,4558 +87,4559,4560 +87,4561,4562 +174,4561,4562 +87,4563,4564 +87,4565,4566 +127,4565,4566 +142,4565,4566 +87,4567,4568 +87,4569,4570 +87,4571,4572 +87,4573,4574 +87,4575,4576 +87,4577,4578 +129,4577,4578 +87,4579,4580 +87,4581,4582 +87,4583,4584 +87,4585,4586 +177,4585,4586 +87,4587,4588 +87,4589,4590 +87,4591,4592 +174,4591,4592 +88,4593,4594 +127,4593,4594 +88,4595,4596 +88,4597,4598 +108,4597,4598 +187,4597,4598 +88,4599,4600 +88,4601,4602 +113,4601,4602 +88,4603,4604 +114,4603,4604 +88,4605,4606 +180,4605,4606 +88,4607,4608 +88,4609,4610 +88,4611,4612 +88,4613,4614 +115,4613,4614 +228,4613,4614 +88,4615,4616 +88,4617,4618 +88,4619,4620 +140,4619,4620 +88,4621,4622 +88,4623,4624 +88,4625,4626 +88,4627,4628 +139,4627,4628 +88,4629,4630 +155,4629,4630 +88,4631,4632 +106,4631,4632 +172,4631,4632 +89,4633,4634 +114,4633,4634 +89,4635,4636 +161,4635,4636 +209,4635,4636 +216,4635,4636 +89,4637,4638 +151,4637,4638 +89,4639,4640 +89,4641,4642 +144,4641,4642 +89,4643,4644 +134,4643,4644 +145,4643,4644 +89,4645,4646 +97,4645,4646 +89,4647,4648 +148,4647,4648 +89,4649,4650 +89,4651,4652 +138,4651,4652 +89,4653,4654 +89,4655,4656 +171,4655,4656 +89,4657,4658 +89,4659,4660 +89,4661,4662 +89,4663,4664 +89,4665,4666 +157,4665,4666 +89,4667,4668 +89,4669,4670 +89,4671,4672 +90,4673,4674 +90,4675,4676 +183,4675,4676 +90,4677,4678 +196,4677,4678 +90,4679,4680 +122,4679,4680 +90,4681,4682 +90,4683,4684 +90,4685,4686 +90,4687,4688 +90,4689,4690 +90,4691,4692 +197,4691,4692 +90,4693,4694 +90,4695,4696 +90,4697,4698 +111,4697,4698 +90,4699,4700 +90,4701,4702 +126,4701,4702 +90,4703,4704 +90,4705,4706 +90,4707,4708 +120,4707,4708 +90,4709,4710 +90,4711,4712 +166,4711,4712 +91,4713,4714 +185,4713,4714 +91,4715,4716 +191,4715,4716 +201,4715,4716 +91,4717,4718 +145,4717,4718 +91,4719,4720 +91,4721,4722 +91,4723,4724 +91,4725,4726 +119,4725,4726 +91,4727,4728 +122,4727,4728 +91,4729,4730 +202,4729,4730 +91,4731,4732 +91,4733,4734 +91,4735,4736 +91,4737,4738 +91,4739,4740 +91,4741,4742 +91,4743,4744 +142,4743,4744 +91,4745,4746 +161,4745,4746 +91,4747,4748 +91,4749,4750 +91,4751,4752 +159,4751,4752 +92,4753,4754 +188,4753,4754 +92,4755,4756 +119,4755,4756 +155,4755,4756 +92,4757,4758 +199,4757,4758 +92,4759,4760 +164,4759,4760 +193,4759,4760 +92,4761,4762 +92,4763,4764 +92,4765,4766 +92,4767,4768 +92,4769,4770 +92,4771,4772 +112,4771,4772 +92,4773,4774 +92,4775,4776 +202,4775,4776 +92,4777,4778 +92,4779,4780 +92,4781,4782 +92,4783,4784 +92,4785,4786 +131,4785,4786 +92,4787,4788 +92,4789,4790 +184,4789,4790 +92,4791,4792 +93,4793,4794 +93,4795,4796 +93,4797,4798 +93,4799,4800 +93,4801,4802 +93,4803,4804 +156,4803,4804 +93,4805,4806 +93,4807,4808 +113,4807,4808 +93,4809,4810 +162,4809,4810 +93,4811,4812 +150,4811,4812 +93,4813,4814 +93,4815,4816 +93,4817,4818 +93,4819,4820 +93,4821,4822 +148,4821,4822 +93,4823,4824 +93,4825,4826 +93,4827,4828 +184,4827,4828 +93,4829,4830 +93,4831,4832 +192,4831,4832 +94,4833,4834 +94,4835,4836 +94,4837,4838 +228,4837,4838 +94,4839,4840 +158,4839,4840 +94,4841,4842 +94,4843,4844 +94,4845,4846 +111,4845,4846 +94,4847,4848 +94,4849,4850 +94,4851,4852 +194,4851,4852 +94,4853,4854 +152,4853,4854 +94,4855,4856 +94,4857,4858 +152,4857,4858 +94,4859,4860 +212,4859,4860 +94,4861,4862 +94,4863,4864 +155,4863,4864 +94,4865,4866 +148,4865,4866 +94,4867,4868 +132,4867,4868 +184,4867,4868 +94,4869,4870 +94,4871,4872 +191,4871,4872 +95,4873,4874 +167,4873,4874 +95,4875,4876 +95,4877,4878 +169,4877,4878 +199,4877,4878 +95,4879,4880 +118,4879,4880 +95,4881,4882 +95,4883,4884 +187,4883,4884 +95,4885,4886 +154,4885,4886 +95,4887,4888 +95,4889,4890 +167,4889,4890 +95,4891,4892 +128,4891,4892 +95,4893,4894 +132,4893,4894 +95,4895,4896 +95,4897,4898 +191,4897,4898 +95,4899,4900 +158,4899,4900 +95,4901,4902 +95,4903,4904 +113,4903,4904 +95,4905,4906 +112,4905,4906 +175,4905,4906 +95,4907,4908 +95,4909,4910 +176,4909,4910 +95,4911,4912 +96,4913,4914 +138,4913,4914 +96,4915,4916 +96,4917,4918 +126,4917,4918 +96,4919,4920 +96,4921,4922 +96,4923,4924 +96,4925,4926 +96,4927,4928 +96,4929,4930 +117,4929,4930 +96,4931,4932 +96,4933,4934 +96,4935,4936 +146,4935,4936 +96,4937,4938 +174,4937,4938 +197,4937,4938 +96,4939,4940 +96,4941,4942 +96,4943,4944 +133,4943,4944 +177,4943,4944 +96,4945,4946 +96,4947,4948 +96,4949,4950 +146,4949,4950 +96,4951,4952 +97,4953,4954 +125,4953,4954 +97,4955,4956 +180,4955,4956 +97,4957,4958 +168,4957,4958 +97,4959,4960 +164,4959,4960 +213,4959,4960 +97,4961,4962 +198,4961,4962 +97,4963,4964 +97,4965,4966 +181,4965,4966 +97,4967,4968 +135,4967,4968 +97,4969,4970 +174,4969,4970 +97,4971,4972 +97,4973,4974 +97,4975,4976 +182,4975,4976 +97,4977,4978 +97,4979,4980 +97,4981,4982 +185,4981,4982 +97,4983,4984 +97,4985,4986 +114,4985,4986 +97,4987,4988 +97,4989,4990 +136,4989,4990 +97,4991,4992 +163,4991,4992 +98,4993,4994 +98,4995,4996 +218,4995,4996 +98,4997,4998 +121,4997,4998 +98,4999,5000 +158,4999,5000 +98,5001,5002 +98,5003,5004 +225,5003,5004 +98,5005,5006 +98,5007,5008 +139,5007,5008 +98,5009,5010 +98,5011,5012 +107,5011,5012 +98,5013,5014 +108,5013,5014 +98,5015,5016 +117,5015,5016 +98,5017,5018 +98,5019,5020 +98,5021,5022 +98,5023,5024 +152,5023,5024 +98,5025,5026 +178,5025,5026 +98,5027,5028 +98,5029,5030 +98,5031,5032 +206,5031,5032 +99,5033,5034 +99,5035,5036 +99,5037,5038 +99,5039,5040 +99,5041,5042 +206,5041,5042 +99,5043,5044 +99,5045,5046 +99,5047,5048 +187,5047,5048 +227,5047,5048 +99,5049,5050 +109,5049,5050 +99,5051,5052 +99,5053,5054 +99,5055,5056 +99,5057,5058 +99,5059,5060 +99,5061,5062 +99,5063,5064 +99,5065,5066 +99,5067,5068 +168,5067,5068 +99,5069,5070 +116,5069,5070 +187,5069,5070 +99,5071,5072 +131,5071,5072 +166,5071,5072 +100,5073,5074 +136,5073,5074 +100,5075,5076 +100,5077,5078 +163,5077,5078 +100,5079,5080 +100,5081,5082 +157,5081,5082 +100,5083,5084 +100,5085,5086 +100,5087,5088 +129,5087,5088 +100,5089,5090 +131,5089,5090 +100,5091,5092 +100,5093,5094 +100,5095,5096 +233,5095,5096 +100,5097,5098 +100,5099,5100 +206,5099,5100 +100,5101,5102 +197,5101,5102 +100,5103,5104 +112,5103,5104 +100,5105,5106 +100,5107,5108 +100,5109,5110 +100,5111,5112 +147,5111,5112 +101,5113,5114 +167,5113,5114 +101,5115,5116 +178,5115,5116 +101,5117,5118 +101,5119,5120 +101,5121,5122 +136,5121,5122 +146,5121,5122 +101,5123,5124 +101,5125,5126 +101,5127,5128 +101,5129,5130 +134,5129,5130 +101,5131,5132 +160,5131,5132 +101,5133,5134 +101,5135,5136 +101,5137,5138 +101,5139,5140 +101,5141,5142 +101,5143,5144 +209,5143,5144 +101,5145,5146 +101,5147,5148 +101,5149,5150 +176,5149,5150 +101,5151,5152 +198,5151,5152 +102,5153,5154 +102,5155,5156 +177,5155,5156 +102,5157,5158 +177,5157,5158 +102,5159,5160 +102,5161,5162 +136,5161,5162 +156,5161,5162 +102,5163,5164 +102,5165,5166 +136,5165,5166 +102,5167,5168 +170,5167,5168 +102,5169,5170 +102,5171,5172 +137,5171,5172 +102,5173,5174 +102,5175,5176 +102,5177,5178 +182,5177,5178 +102,5179,5180 +102,5181,5182 +117,5181,5182 +102,5183,5184 +102,5185,5186 +102,5187,5188 +164,5187,5188 +102,5189,5190 +134,5189,5190 +102,5191,5192 +136,5191,5192 +103,5193,5194 +179,5193,5194 +233,5193,5194 +103,5195,5196 +103,5197,5198 +103,5199,5200 +125,5199,5200 +103,5201,5202 +151,5201,5202 +103,5203,5204 +103,5205,5206 +103,5207,5208 +154,5207,5208 +103,5209,5210 +103,5211,5212 +103,5213,5214 +126,5213,5214 +103,5215,5216 +103,5217,5218 +103,5219,5220 +103,5221,5222 +103,5223,5224 +127,5223,5224 +103,5225,5226 +214,5225,5226 +103,5227,5228 +194,5227,5228 +198,5227,5228 +103,5229,5230 +103,5231,5232 +104,5233,5234 +104,5235,5236 +185,5235,5236 +104,5237,5238 +104,5239,5240 +219,5239,5240 +104,5241,5242 +104,5243,5244 +228,5243,5244 +104,5245,5246 +167,5245,5246 +104,5247,5248 +135,5247,5248 +104,5249,5250 +192,5249,5250 +104,5251,5252 +104,5253,5254 +177,5253,5254 +104,5255,5256 +104,5257,5258 +104,5259,5260 +104,5261,5262 +117,5261,5262 +104,5263,5264 +138,5263,5264 +104,5265,5266 +184,5265,5266 +104,5267,5268 +104,5269,5270 +104,5271,5272 +227,5271,5272 +105,5273,5274 +105,5275,5276 +134,5275,5276 +105,5277,5278 +105,5279,5280 +105,5281,5282 +105,5283,5284 +105,5285,5286 +105,5287,5288 +105,5289,5290 +105,5291,5292 +105,5293,5294 +105,5295,5296 +105,5297,5298 +105,5299,5300 +159,5299,5300 +105,5301,5302 +172,5301,5302 +105,5303,5304 +138,5303,5304 +105,5305,5306 +105,5307,5308 +197,5307,5308 +199,5307,5308 +105,5309,5310 +105,5311,5312 +165,5311,5312 +204,5311,5312 +106,5313,5314 +213,5313,5314 +106,5315,5316 +106,5317,5318 +121,5317,5318 +229,5317,5318 +106,5319,5320 +106,5321,5322 +106,5323,5324 +106,5325,5326 +106,5327,5328 +106,5329,5330 +214,5329,5330 +106,5331,5332 +106,5333,5334 +175,5333,5334 +106,5335,5336 +220,5335,5336 +106,5337,5338 +106,5339,5340 +218,5339,5340 +106,5341,5342 +227,5341,5342 +106,5343,5344 +106,5345,5346 +106,5347,5348 +138,5347,5348 +106,5349,5350 +106,5351,5352 +134,5351,5352 +107,5353,5354 +222,5353,5354 +107,5355,5356 +107,5357,5358 +179,5357,5358 +107,5359,5360 +107,5361,5362 +107,5363,5364 +173,5363,5364 +107,5365,5366 +107,5367,5368 +141,5367,5368 +107,5369,5370 +107,5371,5372 +107,5373,5374 +123,5373,5374 +107,5375,5376 +153,5375,5376 +107,5377,5378 +107,5379,5380 +107,5381,5382 +107,5383,5384 +125,5383,5384 +201,5383,5384 +107,5385,5386 +107,5387,5388 +138,5387,5388 +107,5389,5390 +186,5389,5390 +107,5391,5392 +108,5393,5394 +165,5393,5394 +108,5395,5396 +108,5397,5398 +108,5399,5400 +233,5399,5400 +108,5401,5402 +133,5401,5402 +157,5401,5402 +108,5403,5404 +108,5405,5406 +137,5405,5406 +108,5407,5408 +133,5407,5408 +108,5409,5410 +129,5409,5410 +202,5409,5410 +108,5411,5412 +177,5411,5412 +108,5413,5414 +128,5413,5414 +108,5415,5416 +225,5415,5416 +108,5417,5418 +108,5419,5420 +108,5421,5422 +108,5423,5424 +108,5425,5426 +108,5427,5428 +108,5429,5430 +226,5429,5430 +108,5431,5432 +109,5433,5434 +109,5435,5436 +109,5437,5438 +109,5439,5440 +133,5439,5440 +224,5439,5440 +109,5441,5442 +184,5441,5442 +109,5443,5444 +109,5445,5446 +109,5447,5448 +109,5449,5450 +109,5451,5452 +109,5453,5454 +187,5453,5454 +109,5455,5456 +154,5455,5456 +109,5457,5458 +109,5459,5460 +133,5459,5460 +200,5459,5460 +109,5461,5462 +109,5463,5464 +109,5465,5466 +109,5467,5468 +109,5469,5470 +109,5471,5472 +166,5471,5472 +207,5471,5472 +110,5473,5474 +110,5475,5476 +110,5477,5478 +173,5477,5478 +187,5477,5478 +110,5479,5480 +154,5479,5480 +110,5481,5482 +110,5483,5484 +156,5483,5484 +110,5485,5486 +144,5485,5486 +110,5487,5488 +110,5489,5490 +194,5489,5490 +110,5491,5492 +110,5493,5494 +121,5493,5494 +110,5495,5496 +158,5495,5496 +185,5495,5496 +220,5495,5496 +110,5497,5498 +229,5497,5498 +110,5499,5500 +180,5499,5500 +110,5501,5502 +223,5501,5502 +110,5503,5504 +133,5503,5504 +169,5503,5504 +110,5505,5506 +183,5505,5506 +110,5507,5508 +110,5509,5510 +152,5509,5510 +110,5511,5512 +188,5511,5512 +111,5513,5514 +111,5515,5516 +228,5515,5516 +111,5517,5518 +111,5519,5520 +111,5521,5522 +144,5521,5522 +111,5523,5524 +157,5523,5524 +111,5525,5526 +154,5525,5526 +111,5527,5528 +207,5527,5528 +111,5529,5530 +147,5529,5530 +111,5531,5532 +111,5533,5534 +133,5533,5534 +111,5535,5536 +111,5537,5538 +123,5537,5538 +111,5539,5540 +111,5541,5542 +126,5541,5542 +111,5543,5544 +111,5545,5546 +111,5547,5548 +111,5549,5550 +137,5549,5550 +111,5551,5552 +135,5551,5552 +144,5551,5552 +112,5555,5556 +204,5555,5556 +210,5555,5556 +112,5557,5558 +150,5557,5558 +112,5559,5560 +189,5559,5560 +226,5559,5560 +112,5561,5562 +112,5563,5564 +129,5563,5564 +112,5565,5566 +184,5565,5566 +112,5567,5568 +213,5567,5568 +112,5569,5570 +112,5571,5572 +194,5571,5572 +112,5573,5574 +204,5573,5574 +112,5575,5576 +112,5577,5578 +112,5579,5580 +112,5581,5582 +189,5581,5582 +198,5581,5582 +112,5583,5584 +112,5585,5586 +112,5587,5588 +112,5589,5590 +112,5591,5592 +137,5591,5592 +112,5593,5594 +176,5593,5594 +113,5595,5596 +113,5597,5598 +210,5597,5598 +223,5597,5598 +113,5599,5600 +113,5601,5602 +113,5603,5604 +113,5605,5606 +113,5607,5608 +140,5607,5608 +113,5609,5610 +164,5609,5610 +113,5611,5612 +113,5613,5614 +196,5613,5614 +113,5615,5616 +113,5617,5618 +181,5617,5618 +113,5619,5620 +174,5619,5620 +113,5621,5622 +113,5623,5624 +113,5625,5626 +130,5625,5626 +192,5625,5626 +205,5625,5626 +113,5627,5628 +139,5627,5628 +113,5629,5630 +136,5629,5630 +113,5631,5632 +113,5633,5634 +141,5633,5634 +184,5633,5634 +114,5635,5636 +114,5637,5638 +114,5639,5640 +114,5641,5642 +159,5641,5642 +114,5643,5644 +183,5643,5644 +217,5643,5644 +114,5645,5646 +131,5645,5646 +114,5647,5648 +114,5649,5650 +212,5649,5650 +114,5651,5652 +114,5653,5654 +114,5655,5656 +114,5657,5658 +114,5659,5660 +142,5659,5660 +231,5659,5660 +114,5661,5662 +114,5663,5664 +114,5665,5666 +114,5667,5668 +114,5669,5670 +233,5669,5670 +114,5671,5672 +114,5673,5674 +115,5675,5676 +115,5677,5678 +115,5679,5680 +115,5681,5682 +115,5683,5684 +115,5685,5686 +115,5687,5688 +115,5689,5690 +115,5691,5692 +190,5691,5692 +115,5693,5694 +115,5695,5696 +131,5695,5696 +115,5697,5698 +115,5699,5700 +115,5701,5702 +215,5701,5702 +115,5703,5704 +115,5705,5706 +115,5707,5708 +137,5707,5708 +115,5709,5710 +115,5711,5712 +115,5713,5714 +116,5715,5716 +116,5717,5718 +116,5719,5720 +116,5721,5722 +116,5723,5724 +116,5725,5726 +129,5725,5726 +116,5727,5728 +189,5727,5728 +116,5729,5730 +116,5731,5732 +174,5731,5732 +116,5733,5734 +214,5733,5734 +116,5735,5736 +160,5735,5736 +116,5737,5738 +116,5739,5740 +157,5739,5740 +189,5739,5740 +116,5741,5742 +216,5741,5742 +116,5743,5744 +116,5745,5746 +116,5747,5748 +116,5749,5750 +116,5751,5752 +116,5753,5754 +117,5755,5756 +117,5757,5758 +117,5759,5760 +117,5761,5762 +117,5763,5764 +117,5765,5766 +117,5767,5768 +117,5769,5770 +117,5771,5772 +117,5773,5774 +117,5775,5776 +117,5777,5778 +117,5779,5780 +117,5781,5782 +117,5783,5784 +117,5785,5786 +207,5785,5786 +117,5787,5788 +117,5789,5790 +117,5791,5792 +171,5791,5792 +117,5793,5794 +118,5795,5796 +180,5795,5796 +118,5797,5798 +169,5797,5798 +118,5799,5800 +118,5801,5802 +118,5803,5804 +118,5805,5806 +118,5807,5808 +118,5809,5810 +118,5811,5812 +154,5811,5812 +118,5813,5814 +118,5815,5816 +118,5817,5818 +118,5819,5820 +118,5821,5822 +208,5821,5822 +118,5823,5824 +118,5825,5826 +118,5827,5828 +157,5827,5828 +118,5829,5830 +118,5831,5832 +118,5833,5834 +119,5835,5836 +119,5837,5838 +119,5839,5840 +119,5841,5842 +203,5841,5842 +119,5843,5844 +168,5843,5844 +119,5845,5846 +119,5847,5848 +167,5847,5848 +119,5849,5850 +119,5851,5852 +119,5853,5854 +183,5853,5854 +119,5855,5856 +190,5855,5856 +200,5855,5856 +221,5855,5856 +119,5857,5858 +208,5857,5858 +119,5859,5860 +119,5861,5862 +222,5861,5862 +119,5863,5864 +119,5865,5866 +119,5867,5868 +185,5867,5868 +119,5869,5870 +141,5869,5870 +189,5869,5870 +119,5871,5872 +227,5871,5872 +119,5873,5874 +120,5875,5876 +120,5877,5878 +180,5877,5878 +120,5879,5880 +120,5881,5882 +201,5881,5882 +217,5881,5882 +120,5883,5884 +120,5885,5886 +120,5887,5888 +120,5889,5890 +169,5889,5890 +196,5889,5890 +120,5891,5892 +120,5893,5894 +120,5895,5896 +196,5895,5896 +198,5895,5896 +120,5897,5898 +120,5899,5900 +120,5901,5902 +120,5903,5904 +120,5905,5906 +134,5905,5906 +120,5907,5908 +135,5907,5908 +120,5909,5910 +183,5909,5910 +120,5911,5912 +120,5913,5914 +121,5915,5916 +180,5915,5916 +121,5917,5918 +121,5919,5920 +121,5921,5922 +201,5921,5922 +121,5923,5924 +121,5925,5926 +121,5927,5928 +121,5929,5930 +201,5929,5930 +121,5931,5932 +121,5933,5934 +121,5935,5936 +121,5937,5938 +121,5939,5940 +224,5939,5940 +121,5941,5942 +139,5941,5942 +121,5943,5944 +121,5945,5946 +121,5947,5948 +121,5949,5950 +190,5949,5950 +121,5951,5952 +121,5953,5954 +122,5955,5956 +122,5957,5958 +122,5959,5960 +122,5961,5962 +182,5961,5962 +122,5963,5964 +174,5963,5964 +233,5963,5964 +122,5965,5966 +122,5967,5968 +122,5969,5970 +122,5971,5972 +173,5971,5972 +122,5973,5974 +122,5975,5976 +122,5977,5978 +122,5979,5980 +122,5981,5982 +168,5981,5982 +122,5983,5984 +122,5985,5986 +174,5985,5986 +122,5987,5988 +181,5987,5988 +122,5989,5990 +122,5991,5992 +122,5993,5994 +123,5995,5996 +123,5997,5998 +123,5999,6000 +157,5999,6000 +123,6001,6002 +197,6001,6002 +123,6003,6004 +123,6005,6006 +123,6007,6008 +123,6009,6010 +123,6011,6012 +157,6011,6012 +194,6011,6012 +123,6013,6014 +123,6015,6016 +188,6015,6016 +123,6017,6018 +168,6017,6018 +123,6019,6020 +123,6021,6022 +181,6021,6022 +125,6035,6036 +125,6037,6038 +125,6039,6040 +125,6041,6042 +146,6041,6042 +125,6043,6044 +125,6045,6046 +125,6047,6048 +125,6049,6050 +125,6051,6052 +125,6053,6054 +125,6055,6056 +139,6055,6056 +125,6057,6058 +125,6059,6060 +152,6059,6060 +125,6061,6062 +126,6075,6076 +126,6077,6078 +126,6079,6080 +126,6081,6082 +126,6083,6084 +186,6083,6084 +126,6085,6086 +226,6085,6086 +126,6087,6088 +126,6089,6090 +194,6089,6090 +126,6091,6092 +215,6091,6092 +126,6093,6094 +126,6095,6096 +126,6097,6098 +175,6097,6098 +126,6099,6100 +147,6099,6100 +126,6101,6102 +127,6115,6116 +127,6117,6118 +127,6119,6120 +194,6119,6120 +199,6119,6120 +207,6119,6120 +127,6121,6122 +179,6121,6122 +127,6123,6124 +189,6123,6124 +127,6125,6126 +127,6127,6128 +127,6129,6130 +127,6131,6132 +127,6133,6134 +202,6133,6134 +127,6135,6136 +193,6135,6136 +198,6135,6136 +226,6135,6136 +127,6137,6138 +127,6139,6140 +127,6141,6142 +167,6141,6142 +128,6143,6144 +147,6143,6144 +128,6145,6146 +165,6145,6146 +128,6147,6148 +178,6147,6148 +128,6149,6150 +128,6151,6152 +183,6151,6152 +128,6153,6154 +128,6155,6156 +224,6155,6156 +128,6157,6158 +128,6159,6160 +128,6161,6162 +128,6163,6164 +128,6165,6166 +220,6165,6166 +128,6167,6168 +128,6169,6170 +129,6171,6172 +129,6173,6174 +129,6175,6176 +179,6175,6176 +129,6177,6178 +144,6177,6178 +129,6179,6180 +129,6181,6182 +171,6181,6182 +129,6183,6184 +129,6185,6186 +129,6187,6188 +129,6189,6190 +129,6191,6192 +163,6191,6192 +129,6193,6194 +188,6193,6194 +129,6195,6196 +129,6197,6198 +182,6197,6198 +130,6199,6200 +130,6201,6202 +130,6203,6204 +130,6205,6206 +130,6207,6208 +130,6209,6210 +206,6209,6210 +130,6211,6212 +130,6213,6214 +130,6215,6216 +202,6215,6216 +130,6217,6218 +130,6219,6220 +130,6221,6222 +130,6223,6224 +130,6225,6226 +131,6227,6228 +217,6227,6228 +131,6229,6230 +131,6231,6232 +131,6233,6234 +139,6233,6234 +157,6233,6234 +131,6235,6236 +147,6235,6236 +131,6237,6238 +131,6239,6240 +131,6241,6242 +131,6243,6244 +139,6243,6244 +155,6243,6244 +131,6245,6246 +131,6247,6248 +131,6249,6250 +131,6251,6252 +188,6251,6252 +131,6253,6254 +132,6255,6256 +132,6257,6258 +132,6259,6260 +132,6261,6262 +144,6261,6262 +132,6263,6264 +132,6265,6266 +132,6267,6268 +148,6267,6268 +197,6267,6268 +132,6269,6270 +132,6271,6272 +132,6273,6274 +132,6275,6276 +132,6277,6278 +171,6277,6278 +132,6279,6280 +132,6281,6282 +133,6283,6284 +137,6283,6284 +133,6285,6286 +133,6287,6288 +133,6289,6290 +133,6291,6292 +133,6293,6294 +133,6295,6296 +133,6297,6298 +179,6297,6298 +133,6299,6300 +133,6301,6302 +133,6303,6304 +133,6305,6306 +133,6307,6308 +133,6309,6310 +134,6311,6312 +134,6313,6314 +134,6315,6316 +159,6315,6316 +134,6317,6318 +134,6319,6320 +134,6321,6322 +134,6323,6324 +134,6325,6326 +134,6327,6328 +134,6329,6330 +134,6331,6332 +218,6331,6332 +134,6333,6334 +134,6335,6336 +134,6337,6338 +135,6339,6340 +135,6341,6342 +155,6341,6342 +135,6343,6344 +177,6343,6344 +135,6345,6346 +135,6347,6348 +145,6347,6348 +135,6349,6350 +135,6351,6352 +162,6351,6352 +135,6353,6354 +135,6355,6356 +135,6357,6358 +214,6357,6358 +135,6359,6360 +135,6361,6362 +135,6363,6364 +135,6365,6366 +136,6367,6368 +136,6369,6370 +136,6371,6372 +170,6371,6372 +136,6373,6374 +136,6375,6376 +136,6377,6378 +136,6379,6380 +136,6381,6382 +136,6383,6384 +136,6385,6386 +162,6385,6386 +136,6387,6388 +136,6389,6390 +136,6391,6392 +136,6393,6394 +137,6395,6396 +150,6395,6396 +137,6397,6398 +195,6397,6398 +232,6397,6398 +137,6399,6400 +153,6399,6400 +137,6401,6402 +175,6401,6402 +211,6401,6402 +137,6403,6404 +219,6403,6404 +137,6405,6406 +137,6407,6408 +157,6407,6408 +137,6409,6410 +137,6411,6412 +219,6411,6412 +137,6413,6414 +137,6415,6416 +223,6415,6416 +137,6417,6418 +137,6419,6420 +137,6421,6422 +138,6423,6424 +138,6425,6426 +179,6425,6426 +138,6427,6428 +138,6429,6430 +138,6431,6432 +138,6433,6434 +138,6435,6436 +206,6435,6436 +138,6437,6438 +138,6439,6440 +183,6439,6440 +138,6441,6442 +138,6443,6444 +138,6445,6446 +138,6447,6448 +138,6449,6450 +139,6451,6452 +218,6451,6452 +139,6453,6454 +216,6453,6454 +139,6455,6456 +139,6457,6458 +139,6459,6460 +139,6461,6462 +139,6463,6464 +139,6465,6466 +193,6465,6466 +199,6465,6466 +139,6467,6468 +139,6469,6470 +139,6471,6472 +139,6473,6474 +139,6475,6476 +139,6477,6478 +140,6479,6480 +204,6479,6480 +140,6481,6482 +140,6483,6484 +140,6485,6486 +151,6485,6486 +140,6487,6488 +140,6489,6490 +182,6489,6490 +140,6491,6492 +205,6491,6492 +140,6493,6494 +140,6495,6496 +185,6495,6496 +140,6497,6498 +140,6499,6500 +140,6501,6502 +140,6503,6504 +140,6505,6506 +141,6507,6508 +141,6509,6510 +141,6511,6512 +141,6513,6514 +141,6515,6516 +232,6515,6516 +141,6517,6518 +141,6519,6520 +141,6521,6522 +141,6523,6524 +141,6525,6526 +210,6525,6526 +141,6527,6528 +141,6529,6530 +141,6531,6532 +195,6531,6532 +141,6533,6534 +230,6533,6534 +142,6535,6536 +192,6535,6536 +142,6537,6538 +142,6539,6540 +224,6539,6540 +142,6541,6542 +142,6543,6544 +160,6543,6544 +142,6545,6546 +142,6547,6548 +142,6549,6550 +221,6549,6550 +142,6551,6552 +142,6553,6554 +231,6553,6554 +142,6555,6556 +142,6557,6558 +142,6559,6560 +167,6559,6560 +142,6561,6562 +178,6561,6562 +144,6563,6564 +144,6565,6566 +144,6567,6568 +176,6567,6568 +144,6569,6570 +144,6571,6572 +144,6573,6574 +144,6575,6576 +144,6577,6578 +144,6579,6580 +144,6581,6582 +144,6583,6584 +144,6585,6586 +144,6587,6588 +144,6589,6590 +163,6589,6590 +145,6591,6592 +208,6591,6592 +145,6593,6594 +145,6595,6596 +145,6597,6598 +145,6599,6600 +156,6599,6600 +145,6601,6602 +145,6603,6604 +218,6603,6604 +145,6605,6606 +145,6607,6608 +145,6609,6610 +145,6611,6612 +145,6613,6614 +145,6615,6616 +178,6615,6616 +232,6615,6616 +145,6617,6618 +193,6617,6618 +222,6617,6618 +146,6619,6620 +180,6619,6620 +146,6621,6622 +146,6623,6624 +146,6625,6626 +146,6627,6628 +146,6629,6630 +146,6631,6632 +146,6633,6634 +146,6635,6636 +146,6637,6638 +171,6637,6638 +208,6637,6638 +146,6639,6640 +146,6641,6642 +146,6643,6644 +146,6645,6646 +147,6647,6648 +147,6649,6650 +195,6649,6650 +147,6651,6652 +147,6653,6654 +172,6653,6654 +147,6655,6656 +172,6655,6656 +147,6657,6658 +147,6659,6660 +166,6659,6660 +147,6661,6662 +147,6663,6664 +147,6665,6666 +147,6667,6668 +147,6669,6670 +147,6671,6672 +147,6673,6674 +148,6675,6676 +148,6677,6678 +170,6677,6678 +148,6679,6680 +148,6681,6682 +180,6681,6682 +148,6683,6684 +148,6685,6686 +148,6687,6688 +148,6689,6690 +202,6689,6690 +148,6691,6692 +148,6693,6694 +148,6695,6696 +148,6697,6698 +148,6699,6700 +148,6701,6702 +149,6703,6704 +149,6705,6706 +149,6707,6708 +149,6709,6710 +149,6711,6712 +149,6713,6714 +149,6715,6716 +204,6715,6716 +149,6717,6718 +149,6719,6720 +149,6721,6722 +149,6723,6724 +149,6725,6726 +186,6725,6726 +149,6727,6728 +149,6729,6730 +150,6731,6732 +150,6733,6734 +150,6735,6736 +222,6735,6736 +150,6737,6738 +150,6739,6740 +184,6739,6740 +150,6741,6742 +211,6741,6742 +150,6743,6744 +221,6743,6744 +150,6745,6746 +150,6747,6748 +222,6747,6748 +150,6749,6750 +187,6749,6750 +150,6751,6752 +150,6753,6754 +150,6755,6756 +150,6757,6758 +151,6759,6760 +151,6761,6762 +162,6761,6762 +151,6763,6764 +151,6765,6766 +151,6767,6768 +151,6769,6770 +151,6771,6772 +172,6771,6772 +151,6773,6774 +151,6775,6776 +151,6777,6778 +151,6779,6780 +151,6781,6782 +151,6783,6784 +175,6783,6784 +151,6785,6786 +204,6785,6786 +152,6787,6788 +152,6789,6790 +197,6789,6790 +152,6791,6792 +152,6793,6794 +152,6795,6796 +166,6795,6796 +152,6797,6798 +152,6799,6800 +213,6799,6800 +152,6801,6802 +214,6801,6802 +152,6803,6804 +162,6803,6804 +152,6805,6806 +152,6807,6808 +152,6809,6810 +212,6809,6810 +152,6811,6812 +162,6811,6812 +225,6811,6812 +152,6813,6814 +153,6815,6816 +153,6817,6818 +153,6819,6820 +153,6821,6822 +177,6821,6822 +153,6823,6824 +153,6825,6826 +153,6827,6828 +153,6829,6830 +188,6829,6830 +153,6831,6832 +153,6833,6834 +153,6835,6836 +153,6837,6838 +153,6839,6840 +153,6841,6842 +212,6841,6842 +154,6843,6844 +221,6843,6844 +154,6845,6846 +180,6845,6846 +154,6847,6848 +154,6849,6850 +154,6851,6852 +154,6853,6854 +154,6855,6856 +154,6857,6858 +154,6859,6860 +154,6861,6862 +154,6863,6864 +154,6865,6866 +189,6865,6866 +154,6867,6868 +154,6869,6870 +155,6871,6872 +155,6873,6874 +188,6873,6874 +155,6875,6876 +155,6877,6878 +155,6879,6880 +155,6881,6882 +155,6883,6884 +155,6885,6886 +155,6887,6888 +155,6889,6890 +155,6891,6892 +155,6893,6894 +155,6895,6896 +172,6895,6896 +155,6897,6898 +212,6897,6898 +228,6897,6898 +156,6899,6900 +156,6901,6902 +156,6903,6904 +156,6905,6906 +176,6905,6906 +156,6907,6908 +156,6909,6910 +156,6911,6912 +156,6913,6914 +156,6915,6916 +156,6917,6918 +214,6917,6918 +156,6919,6920 +156,6921,6922 +220,6921,6922 +156,6923,6924 +156,6925,6926 +157,6927,6928 +157,6929,6930 +173,6929,6930 +157,6931,6932 +157,6933,6934 +157,6935,6936 +157,6937,6938 +157,6939,6940 +157,6941,6942 +232,6941,6942 +157,6943,6944 +157,6945,6946 +157,6947,6948 +157,6949,6950 +157,6951,6952 +157,6953,6954 +158,6955,6956 +210,6955,6956 +158,6957,6958 +158,6959,6960 +158,6961,6962 +158,6963,6964 +158,6965,6966 +158,6967,6968 +158,6969,6970 +158,6971,6972 +158,6973,6974 +222,6973,6974 +158,6975,6976 +158,6977,6978 +158,6979,6980 +158,6981,6982 +159,6983,6984 +159,6985,6986 +159,6987,6988 +176,6987,6988 +159,6989,6990 +159,6991,6992 +159,6993,6994 +195,6993,6994 +159,6995,6996 +159,6997,6998 +159,6999,7000 +159,7001,7002 +159,7003,7004 +159,7005,7006 +183,7005,7006 +159,7007,7008 +199,7007,7008 +159,7009,7010 +170,7009,7010 +198,7009,7010 +160,7011,7012 +160,7013,7014 +160,7015,7016 +160,7017,7018 +160,7019,7020 +160,7021,7022 +160,7023,7024 +160,7025,7026 +160,7027,7028 +160,7029,7030 +217,7029,7030 +160,7031,7032 +160,7033,7034 +160,7035,7036 +187,7035,7036 +160,7037,7038 +161,7039,7040 +161,7041,7042 +231,7041,7042 +161,7043,7044 +179,7043,7044 +161,7045,7046 +161,7047,7048 +161,7049,7050 +161,7051,7052 +161,7053,7054 +195,7053,7054 +161,7055,7056 +161,7057,7058 +161,7059,7060 +161,7061,7062 +191,7061,7062 +161,7063,7064 +161,7065,7066 +162,7067,7068 +162,7069,7070 +231,7069,7070 +162,7071,7072 +162,7073,7074 +162,7075,7076 +162,7077,7078 +162,7079,7080 +228,7079,7080 +162,7081,7082 +195,7081,7082 +162,7083,7084 +162,7085,7086 +223,7085,7086 +162,7087,7088 +162,7089,7090 +162,7091,7092 +162,7093,7094 +163,7095,7096 +163,7097,7098 +163,7099,7100 +230,7099,7100 +163,7101,7102 +163,7103,7104 +163,7105,7106 +163,7107,7108 +163,7109,7110 +201,7109,7110 +163,7111,7112 +163,7113,7114 +190,7113,7114 +163,7115,7116 +163,7117,7118 +163,7119,7120 +163,7121,7122 +164,7123,7124 +164,7125,7126 +164,7127,7128 +164,7129,7130 +186,7129,7130 +164,7131,7132 +164,7133,7134 +164,7135,7136 +164,7137,7138 +196,7137,7138 +164,7139,7140 +223,7139,7140 +164,7141,7142 +164,7143,7144 +164,7145,7146 +164,7147,7148 +164,7149,7150 +165,7151,7152 +165,7153,7154 +165,7155,7156 +196,7155,7156 +165,7157,7158 +165,7159,7160 +165,7161,7162 +165,7163,7164 +165,7165,7166 +165,7167,7168 +165,7169,7170 +165,7171,7172 +165,7173,7174 +165,7175,7176 +165,7177,7178 +166,7179,7180 +166,7181,7182 +166,7183,7184 +166,7185,7186 +166,7187,7188 +166,7189,7190 +166,7191,7192 +166,7193,7194 +166,7195,7196 +166,7197,7198 +166,7199,7200 +166,7201,7202 +166,7203,7204 +166,7205,7206 +167,7207,7208 +167,7209,7210 +167,7211,7212 +167,7213,7214 +167,7215,7216 +206,7215,7216 +167,7217,7218 +167,7219,7220 +167,7221,7222 +167,7223,7224 +167,7225,7226 +167,7227,7228 +167,7229,7230 +167,7231,7232 +167,7233,7234 +192,7233,7234 +168,7235,7236 +168,7237,7238 +168,7239,7240 +168,7241,7242 +168,7243,7244 +168,7245,7246 +168,7247,7248 +168,7249,7250 +168,7251,7252 +168,7253,7254 +168,7255,7256 +168,7257,7258 +168,7259,7260 +168,7261,7262 +169,7263,7264 +169,7265,7266 +169,7267,7268 +169,7269,7270 +169,7271,7272 +184,7271,7272 +169,7273,7274 +169,7275,7276 +169,7277,7278 +169,7279,7280 +169,7281,7282 +169,7283,7284 +169,7285,7286 +169,7287,7288 +169,7289,7290 +200,7289,7290 +170,7291,7292 +170,7293,7294 +170,7295,7296 +170,7297,7298 +170,7299,7300 +170,7301,7302 +170,7303,7304 +170,7305,7306 +170,7307,7308 +170,7309,7310 +170,7311,7312 +170,7313,7314 +170,7315,7316 +170,7317,7318 +171,7319,7320 +171,7321,7322 +171,7323,7324 +171,7325,7326 +171,7327,7328 +171,7329,7330 +171,7331,7332 +200,7331,7332 +171,7333,7334 +171,7335,7336 +171,7337,7338 +171,7339,7340 +171,7341,7342 +190,7341,7342 +171,7343,7344 +171,7345,7346 +172,7347,7348 +199,7347,7348 +172,7349,7350 +172,7351,7352 +172,7353,7354 +172,7355,7356 +227,7355,7356 +172,7357,7358 +172,7359,7360 +172,7361,7362 +172,7363,7364 +172,7365,7366 +172,7367,7368 +172,7369,7370 +172,7371,7372 +172,7373,7374 +173,7375,7376 +173,7377,7378 +173,7379,7380 +209,7379,7380 +211,7379,7380 +173,7381,7382 +173,7383,7384 +173,7385,7386 +227,7385,7386 +173,7387,7388 +173,7389,7390 +173,7391,7392 +173,7393,7394 +173,7395,7396 +211,7395,7396 +173,7397,7398 +173,7399,7400 +173,7401,7402 +232,7401,7402 +174,7403,7404 +174,7405,7406 +174,7407,7408 +174,7409,7410 +174,7411,7412 +174,7413,7414 +174,7415,7416 +174,7417,7418 +174,7419,7420 +174,7421,7422 +174,7423,7424 +174,7425,7426 +174,7427,7428 +174,7429,7430 +175,7431,7432 +175,7433,7434 +209,7433,7434 +175,7435,7436 +175,7437,7438 +175,7439,7440 +175,7441,7442 +175,7443,7444 +175,7445,7446 +175,7447,7448 +175,7449,7450 +175,7451,7452 +175,7453,7454 +175,7455,7456 +175,7457,7458 +232,7457,7458 +176,7459,7460 +176,7461,7462 +176,7463,7464 +176,7465,7466 +176,7467,7468 +176,7469,7470 +176,7471,7472 +176,7473,7474 +176,7475,7476 +176,7477,7478 +176,7479,7480 +176,7481,7482 +176,7483,7484 +176,7485,7486 +177,7487,7488 +177,7489,7490 +204,7489,7490 +177,7491,7492 +186,7491,7492 +177,7493,7494 +177,7495,7496 +177,7497,7498 +177,7499,7500 +177,7501,7502 +177,7503,7504 +177,7505,7506 +177,7507,7508 +177,7509,7510 +177,7511,7512 +177,7513,7514 +178,7515,7516 +178,7517,7518 +178,7519,7520 +178,7521,7522 +178,7523,7524 +178,7525,7526 +178,7527,7528 +178,7529,7530 +178,7531,7532 +178,7533,7534 +178,7535,7536 +203,7535,7536 +178,7537,7538 +178,7539,7540 +178,7541,7542 +209,7541,7542 +179,7543,7544 +179,7545,7546 +179,7547,7548 +179,7549,7550 +179,7551,7552 +179,7553,7554 +179,7555,7556 +209,7555,7556 +179,7557,7558 +179,7559,7560 +179,7561,7562 +179,7563,7564 +179,7565,7566 +179,7567,7568 +179,7569,7570 +180,7571,7572 +180,7573,7574 +180,7575,7576 +180,7577,7578 +180,7579,7580 +180,7581,7582 +180,7583,7584 +180,7585,7586 +180,7587,7588 +180,7589,7590 +180,7591,7592 +180,7593,7594 +180,7595,7596 +180,7597,7598 +181,7599,7600 +181,7601,7602 +181,7603,7604 +181,7605,7606 +181,7607,7608 +181,7609,7610 +215,7609,7610 +181,7611,7612 +181,7613,7614 +181,7615,7616 +219,7615,7616 +181,7617,7618 +181,7619,7620 +181,7621,7622 +181,7623,7624 +181,7625,7626 +182,7627,7628 +182,7629,7630 +182,7631,7632 +182,7633,7634 +202,7633,7634 +182,7635,7636 +182,7637,7638 +182,7639,7640 +182,7641,7642 +182,7643,7644 +182,7645,7646 +194,7645,7646 +182,7647,7648 +182,7649,7650 +182,7651,7652 +182,7653,7654 +183,7655,7656 +183,7657,7658 +183,7659,7660 +183,7661,7662 +183,7663,7664 +183,7665,7666 +183,7667,7668 +183,7669,7670 +183,7671,7672 +183,7673,7674 +193,7673,7674 +183,7675,7676 +183,7677,7678 +183,7679,7680 +183,7681,7682 +184,7683,7684 +184,7685,7686 +184,7687,7688 +184,7689,7690 +184,7691,7692 +184,7693,7694 +184,7695,7696 +184,7697,7698 +184,7699,7700 +184,7701,7702 +184,7703,7704 +184,7705,7706 +184,7707,7708 +184,7709,7710 +185,7711,7712 +212,7711,7712 +185,7713,7714 +185,7715,7716 +185,7717,7718 +185,7719,7720 +185,7721,7722 +185,7723,7724 +185,7725,7726 +185,7727,7728 +185,7729,7730 +185,7731,7732 +185,7733,7734 +185,7735,7736 +185,7737,7738 +186,7739,7740 +186,7741,7742 +186,7743,7744 +186,7745,7746 +186,7747,7748 +186,7749,7750 +186,7751,7752 +186,7753,7754 +186,7755,7756 +186,7757,7758 +186,7759,7760 +186,7761,7762 +186,7763,7764 +186,7765,7766 +187,7767,7768 +187,7769,7770 +187,7771,7772 +187,7773,7774 +187,7775,7776 +187,7777,7778 +187,7779,7780 +187,7781,7782 +187,7783,7784 +187,7785,7786 +187,7787,7788 +187,7789,7790 +187,7791,7792 +187,7793,7794 +188,7795,7796 +188,7797,7798 +188,7799,7800 +188,7801,7802 +188,7803,7804 +188,7805,7806 +188,7807,7808 +188,7809,7810 +188,7811,7812 +188,7813,7814 +189,7815,7816 +189,7817,7818 +189,7819,7820 +189,7821,7822 +189,7823,7824 +189,7825,7826 +189,7827,7828 +189,7829,7830 +189,7831,7832 +225,7831,7832 +189,7833,7834 +190,7835,7836 +190,7837,7838 +190,7839,7840 +190,7841,7842 +190,7843,7844 +190,7845,7846 +190,7847,7848 +190,7849,7850 +190,7851,7852 +190,7853,7854 +191,7855,7856 +191,7857,7858 +224,7857,7858 +191,7859,7860 +191,7861,7862 +191,7863,7864 +191,7865,7866 +191,7867,7868 +191,7869,7870 +191,7871,7872 +191,7873,7874 +192,7875,7876 +192,7877,7878 +192,7879,7880 +192,7881,7882 +192,7883,7884 +192,7885,7886 +192,7887,7888 +192,7889,7890 +192,7891,7892 +192,7893,7894 +214,7893,7894 +193,7895,7896 +193,7897,7898 +193,7899,7900 +193,7901,7902 +193,7903,7904 +193,7905,7906 +193,7907,7908 +193,7909,7910 +193,7911,7912 +193,7913,7914 +194,7915,7916 +194,7917,7918 +194,7919,7920 +194,7921,7922 +194,7923,7924 +194,7925,7926 +194,7927,7928 +194,7929,7930 +194,7931,7932 +194,7933,7934 +195,7935,7936 +195,7937,7938 +195,7939,7940 +195,7941,7942 +205,7941,7942 +195,7943,7944 +195,7945,7946 +195,7947,7948 +195,7949,7950 +195,7951,7952 +230,7951,7952 +195,7953,7954 +196,7955,7956 +196,7957,7958 +196,7959,7960 +196,7961,7962 +196,7963,7964 +196,7965,7966 +196,7967,7968 +196,7969,7970 +196,7971,7972 +196,7973,7974 +197,7975,7976 +221,7975,7976 +197,7977,7978 +197,7979,7980 +197,7981,7982 +197,7983,7984 +197,7985,7986 +197,7987,7988 +197,7989,7990 +197,7991,7992 +197,7993,7994 +198,7995,7996 +198,7997,7998 +198,7999,8000 +198,8001,8002 +198,8003,8004 +198,8005,8006 +198,8007,8008 +198,8009,8010 +198,8011,8012 +198,8013,8014 +223,8013,8014 +199,8015,8016 +199,8017,8018 +199,8019,8020 +199,8021,8022 +199,8023,8024 +199,8025,8026 +199,8027,8028 +199,8029,8030 +199,8031,8032 +199,8033,8034 +200,8035,8036 +200,8037,8038 +200,8039,8040 +200,8041,8042 +200,8043,8044 +200,8045,8046 +200,8047,8048 +200,8049,8050 +200,8051,8052 +200,8053,8054 +201,8055,8056 +201,8057,8058 +201,8059,8060 +201,8061,8062 +201,8063,8064 +201,8065,8066 +201,8067,8068 +201,8069,8070 +201,8071,8072 +201,8073,8074 +202,8075,8076 +202,8077,8078 +202,8079,8080 +202,8081,8082 +202,8083,8084 +202,8085,8086 +202,8087,8088 +222,8087,8088 +202,8089,8090 +202,8091,8092 +202,8093,8094 +224,8093,8094 +203,8095,8096 +203,8097,8098 +203,8099,8100 +203,8101,8102 +203,8103,8104 +203,8105,8106 +203,8107,8108 +203,8109,8110 +203,8111,8112 +203,8113,8114 +204,8115,8116 +204,8117,8118 +204,8119,8120 +204,8121,8122 +204,8123,8124 +204,8125,8126 +204,8127,8128 +204,8129,8130 +204,8131,8132 +204,8133,8134 +205,8135,8136 +205,8137,8138 +205,8139,8140 +205,8141,8142 +205,8143,8144 +205,8145,8146 +205,8147,8148 +205,8149,8150 +205,8151,8152 +205,8153,8154 +206,8155,8156 +206,8157,8158 +206,8159,8160 +206,8161,8162 +206,8163,8164 +206,8165,8166 +206,8167,8168 +206,8169,8170 +206,8171,8172 +206,8173,8174 +207,8177,8178 +207,8179,8180 +207,8181,8182 +207,8183,8184 +207,8185,8186 +207,8187,8188 +207,8189,8190 +207,8191,8192 +207,8193,8194 +207,8195,8196 +208,8197,8198 +208,8199,8200 +208,8201,8202 +208,8203,8204 +208,8205,8206 +208,8207,8208 +208,8209,8210 +208,8211,8212 +208,8213,8214 +208,8215,8216 +209,8217,8218 +233,8217,8218 +209,8219,8220 +209,8221,8222 +209,8223,8224 +209,8225,8226 +209,8227,8228 +209,8229,8230 +209,8231,8232 +209,8233,8234 +209,8235,8236 +210,8237,8238 +210,8239,8240 +210,8241,8242 +210,8243,8244 +210,8245,8246 +210,8247,8248 +210,8249,8250 +210,8251,8252 +210,8253,8254 +210,8255,8256 +211,8257,8258 +211,8259,8260 +211,8261,8262 +211,8263,8264 +211,8265,8266 +211,8267,8268 +211,8269,8270 +211,8271,8272 +211,8273,8274 +211,8275,8276 +212,8277,8278 +212,8279,8280 +212,8281,8282 +212,8283,8284 +212,8285,8286 +212,8287,8288 +212,8289,8290 +212,8291,8292 +212,8293,8294 +212,8295,8296 +213,8297,8298 +213,8299,8300 +213,8301,8302 +213,8303,8304 +213,8305,8306 +213,8307,8308 +213,8309,8310 +213,8311,8312 +213,8313,8314 +213,8315,8316 +214,8317,8318 +214,8319,8320 +214,8321,8322 +214,8323,8324 +214,8325,8326 +214,8327,8328 +214,8329,8330 +214,8331,8332 +214,8333,8334 +214,8335,8336 +215,8337,8338 +215,8339,8340 +215,8341,8342 +215,8343,8344 +215,8345,8346 +215,8347,8348 +215,8349,8350 +215,8351,8352 +215,8353,8354 +215,8355,8356 +216,8357,8358 +216,8359,8360 +216,8361,8362 +216,8363,8364 +216,8365,8366 +216,8367,8368 +216,8369,8370 +216,8371,8372 +216,8373,8374 +216,8375,8376 +217,8377,8378 +217,8379,8380 +217,8381,8382 +217,8383,8384 +217,8385,8386 +217,8387,8388 +217,8389,8390 +217,8391,8392 +217,8393,8394 +217,8395,8396 +218,8397,8398 +218,8399,8400 +218,8401,8402 +218,8403,8404 +218,8405,8406 +218,8407,8408 +218,8409,8410 +218,8411,8412 +218,8413,8414 +218,8415,8416 +219,8417,8418 +219,8419,8420 +219,8421,8422 +219,8423,8424 +219,8425,8426 +219,8427,8428 +219,8429,8430 +219,8431,8432 +219,8433,8434 +219,8435,8436 +220,8437,8438 +220,8439,8440 +220,8441,8442 +220,8443,8444 +220,8445,8446 +220,8447,8448 +220,8449,8450 +220,8451,8452 +220,8453,8454 +220,8455,8456 +221,8457,8458 +221,8459,8460 +221,8461,8462 +221,8463,8464 +221,8465,8466 +221,8467,8468 +221,8469,8470 +221,8471,8472 +221,8473,8474 +221,8475,8476 +222,8477,8478 +222,8479,8480 +222,8481,8482 +222,8483,8484 +222,8485,8486 +227,8485,8486 +222,8487,8488 +222,8489,8490 +222,8491,8492 +222,8493,8494 +222,8495,8496 +223,8497,8498 +223,8499,8500 +223,8501,8502 +223,8503,8504 +223,8505,8506 +223,8507,8508 +223,8509,8510 +223,8511,8512 +223,8513,8514 +223,8515,8516 +224,8517,8518 +224,8519,8520 +224,8521,8522 +224,8523,8524 +224,8525,8526 +224,8527,8528 +224,8529,8530 +224,8531,8532 +224,8533,8534 +224,8535,8536 +225,8537,8538 +225,8539,8540 +225,8541,8542 +225,8543,8544 +225,8545,8546 +225,8547,8548 +225,8549,8550 +225,8551,8552 +225,8553,8554 +225,8555,8556 +226,8557,8558 +230,8557,8558 +226,8559,8560 +226,8561,8562 +226,8563,8564 +226,8565,8566 +226,8567,8568 +226,8569,8570 +226,8571,8572 +226,8573,8574 +226,8575,8576 +227,8577,8578 +227,8579,8580 +227,8581,8582 +227,8583,8584 +227,8585,8586 +227,8587,8588 +227,8589,8590 +227,8591,8592 +227,8593,8594 +227,8595,8596 +228,8597,8598 +228,8599,8600 +228,8601,8602 +228,8603,8604 +228,8605,8606 +228,8607,8608 +228,8609,8610 +228,8611,8612 +228,8613,8614 +228,8615,8616 +229,8617,8618 +229,8619,8620 +229,8621,8622 +229,8623,8624 +229,8625,8626 +231,8625,8626 +229,8627,8628 +229,8629,8630 +229,8631,8632 +229,8633,8634 +229,8635,8636 +230,8637,8638 +230,8639,8640 +230,8641,8642 +230,8643,8644 +230,8645,8646 +230,8647,8648 +230,8649,8650 +230,8651,8652 +230,8653,8654 +230,8655,8656 +231,8657,8658 +231,8659,8660 +231,8661,8662 +231,8663,8664 +231,8665,8666 +231,8667,8668 +231,8669,8670 +231,8671,8672 +231,8673,8674 +231,8675,8676 +232,8677,8678 +232,8679,8680 +232,8681,8682 +232,8683,8684 +232,8685,8686 +232,8687,8688 +232,8689,8690 +232,8691,8692 +232,8693,8694 +232,8695,8696 +233,8697,8698 +233,8699,8700 +233,8701,8702 +233,8703,8704 +233,8705,8706 +233,8707,8708 +233,8709,8710 +233,8711,8712 +233,8713,8714 +233,8715,8716 \ No newline at end of file diff --git a/examples/file_read_stream/lib/words.csv b/examples/file_read_stream/lib/words.csv new file mode 100644 index 0000000..2e24435 --- /dev/null +++ b/examples/file_read_stream/lib/words.csv @@ -0,0 +1,7971 @@ +1,anguish,3,1 +2,beloved,3,1 +3,cleanse,3,1 +4,cloak,3,1 +5,daybreak,3,1 +6,deed,3,1 +7,dwell,3,1 +8,harsh,3,1 +9,indeed,3,1 +10,jackal,3,1 +11,loincloth,3,1 +12,mercy,3,1 +13,partake,3,1 +14,protrude,3,1 +15,quench,3,1 +16,slender,3,1 +17,sorrow,3,1 +18,spare,3,1 +19,sprout,3,1 +20,stiff,3,1 +21,tan,3,1 +22,thorny,3,1 +23,thus,3,1 +24,unsound,3,1 +25,vain,3,1 +26,wail,3,1 +27,wane,3,1 +28,weave,3,1 +29,worship,3,1 +30,abound,3,1 +31,brewery,3,1 +32,cattle,3,1 +33,doable,3,1 +34,eerily,3,1 +35,expertise,3,1 +36,flick,3,1 +37,flung,3,1 +38,gibberish,3,1 +39,greedy,3,1 +40,halo,3,1 +41,harnessed,3,1 +42,inception,3,1 +43,jockeying,3,1 +44,limelight,3,1 +45,loan,3,1 +46,napkin,3,1 +47,pivotal,3,1 +48,plywood,3,1 +49,raw,3,1 +50,recipe,3,1 +51,saddle,3,1 +52,slick,3,1 +53,sprinkle,3,1 +54,stagger,3,1 +55,stray,3,1 +56,thaw,3,1 +57,thorough,3,1 +58,topple,3,1 +59,turmoil,3,1 +60,beneath,3,1 +61,bias,3,1 +62,brag,3,1 +63,breadth,3,1 +64,bury,3,1 +65,cope,3,1 +66,cramp,3,1 +67,deity,3,1 +68,embankment,3,1 +69,ferry,3,1 +70,foible,3,1 +71,garment,3,1 +72,grief,3,1 +73,handkerchief,3,1 +74,heed,3,1 +75,hone,3,1 +76,hump,3,1 +77,junkie,3,1 +78,loathe,3,1 +79,meekly,3,1 +80,mower,3,1 +81,oblivious,3,1 +82,riddle,3,1 +83,shelf,3,1 +84,soil,3,1 +85,sturdy,3,1 +86,tantrum,3,1 +87,trite,3,1 +88,warrant,3,1 +89,yield,3,1 +90,aisle,3,1 +91,aptly,3,1 +92,awe,3,1 +93,blunt,3,1 +94,crosswalk,3,1 +95,crummy,3,1 +96,cumbersome,3,1 +97,dogged,3,1 +98,evict,3,1 +99,fad,3,1 +100,flimsy,3,1 +101,gait,3,1 +102,gamble,3,1 +103,gruff,3,1 +104,hamper,3,1 +105,havoc,3,1 +106,jar,3,1 +107,laggard,3,1 +108,nuance,3,1 +109,obnoxious,3,1 +110,oust,3,1 +111,rebate,3,1 +112,renown,3,1 +113,rogue,3,1 +114,sleek,3,1 +115,steer,3,1 +116,stir,3,1 +117,surefire,3,1 +118,sway,3,1 +119,thrive,3,1 +120,abide,3,1 +121,awry,3,1 +122,balk,3,1 +123,battered,3,1 +124,byword,3,1 +125,chuckle,3,1 +126,clench,3,1 +127,clutter,3,1 +128,cram,3,1 +129,crave,3,1 +130,daunt,3,1 +131,devise,3,1 +132,ditch,3,1 +133,dumbfounded,3,1 +134,dusk,3,1 +135,enroll,3,1 +136,falter,3,1 +137,iffy,3,1 +138,loot,3,1 +139,marble,3,1 +140,pal,3,1 +141,pitch,3,1 +142,plead,3,1 +143,ruthless,3,1 +144,scant,3,1 +145,sin,3,1 +146,steep,3,1 +147,strive,3,1 +148,twitch,3,1 +149,wiggle,3,1 +150,albeit,3,1 +151,amuck,3,1 +152,behalf,3,1 +153,bribe,3,1 +154,cinch,3,1 +155,claim,3,1 +156,curb,3,1 +157,dabble,3,1 +158,ditto,3,1 +159,dross,3,1 +160,drought,3,1 +161,drudgery,3,1 +162,entail,3,1 +163,froth,3,1 +164,guise,3,1 +165,haze,3,1 +166,hence,3,1 +167,hiccups,3,1 +168,natch,3,1 +169,pond,3,1 +170,rear,3,1 +171,rehearse,3,1 +172,sloth,3,1 +173,splinter,3,1 +174,swell,3,1 +175,tow,3,1 +176,utterly,3,1 +177,wax,3,1 +178,wheat,3,1 +179,wily,3,1 +180,backdrop,3,1 +181,bog,3,1 +182,brash,3,1 +183,clog,3,1 +184,curl,3,1 +185,dismayed,3,1 +186,enhance,3,1 +187,exploit,3,1 +188,farfetched,3,1 +189,foster,3,1 +190,glitzy,3,1 +191,haphazard,3,1 +192,irksome,3,1 +193,lavish,3,1 +194,leash,3,1 +195,mugged,3,1 +196,n-fold,3,1 +197,notorious,3,1 +198,patent,3,1 +199,pesky,3,1 +200,ramble,3,1 +201,skew,3,1 +202,slope,3,1 +203,sloppy,3,1 +204,startle,3,1 +205,tailor,3,1 +206,tinker,3,1 +207,wary,3,1 +208,wellspring,3,1 +209,wry,3,1 +210,briefing,3,1 +211,clay,3,1 +212,cranny,3,1 +213,cripple,3,1 +214,delve,3,1 +215,demeanor,3,1 +216,disguise,3,1 +217,fickle,3,1 +218,haystack,3,1 +219,hum,3,1 +220,liable,3,1 +221,loafing,3,1 +222,looming,3,1 +223,nimble,3,1 +224,nook,3,1 +225,paramount,3,1 +226,penchant,3,1 +227,pitiful,3,1 +228,plaint,3,1 +229,ploy,3,1 +230,rummage,3,1 +231,rustle,3,1 +232,sewn,3,1 +233,spur,3,1 +234,stifle,3,1 +235,stubborn,3,1 +236,swindle,3,1 +237,tidbit,3,1 +238,welfare,3,1 +239,whereabouts,3,1 +240,bail,3,1 +241,bush,3,1 +242,coarse,3,1 +243,copper,3,1 +244,dawdle,3,1 +245,elicit,3,1 +246,fluke,3,1 +247,gloss,3,1 +248,halt,3,1 +249,hoard,3,1 +250,instance,3,1 +251,latch,3,1 +252,lurk,3,1 +253,nevertheless,3,1 +254,nurture,3,1 +255,pitfall,3,1 +256,prowess,3,1 +257,relapse,3,1 +258,reverie,3,1 +259,shrunk,3,1 +260,skim,3,1 +261,staircase,3,1 +262,tantalizing,3,1 +263,toss,3,1 +264,vicious,3,1 +265,wacky,3,1 +266,ward,3,1 +267,whiff,3,1 +268,wither,3,1 +269,wrinkle,3,1 +270,ash,3,1 +271,blanket,3,1 +272,boiler,3,1 +273,broom,3,1 +274,cabinet,3,1 +275,chandelier,3,1 +276,crockery,3,1 +277,cushion,3,1 +278,dish rack,3,1 +279,doorknob,3,1 +280,dove,3,1 +281,drawer,3,1 +282,ember,3,1 +283,footrest,3,1 +284,grumble,3,1 +285,hand,3,1 +286,keyhole,3,1 +287,knick-knack,3,1 +288,oven,3,1 +289,pantry,3,1 +290,radiator,3,1 +291,rucksack,3,1 +292,shutter,3,1 +293,sink,3,1 +294,slipper,3,1 +295,socket,3,1 +296,tap / faucet,3,1 +297,tile,3,1 +298,washtub,3,1 +299,woodstove,3,1 +300,apron,3,1 +301,barrow,3,1 +302,beech,3,1 +303,birch,3,1 +304,blackbird,3,1 +305,coal,3,1 +306,creeper,3,1 +307,fence,3,1 +308,hammock,3,1 +309,harvest,3,1 +310,hay,3,1 +311,hedge,3,1 +312,hoe,3,1 +313,hornet,3,1 +314,limb,3,1 +315,manure,3,1 +316,mud,3,1 +317,pebble,3,1 +318,pickaxe,3,1 +319,plow,3,1 +320,porch,3,1 +321,prune,3,1 +322,rake,3,1 +323,seesaw,3,1 +324,shrub,3,1 +325,sow,3,1 +326,spade,3,1 +327,straw,3,1 +328,terrace,3,1 +329,windowsill,3,1 +330,advance,3,1 +331,arduous,3,1 +332,arrival,3,1 +333,baggage,3,1 +334,book,3,1 +335,bureau,3,1 +336,busboy,3,1 +337,depart,3,1 +338,dispatch,3,1 +339,endorse,3,1 +340,expiry,3,1 +341,foreign,3,1 +342,harbor,3,1 +343,heady,3,1 +344,hitchhiking,3,1 +345,invigorate,3,1 +346,landing,3,1 +347,outward,3,1 +348,overseas,3,1 +349,pedestrian,3,1 +350,postpone,3,1 +351,sail,3,1 +352,sidewalk,3,1 +353,substantiate,3,1 +354,takeoff,3,1 +355,tollbooth,3,1 +356,toll,3,1 +357,troubled,3,1 +358,venture,3,1 +359,windshield,3,1 +360,adjournment,3,1 +361,apprentice,3,1 +362,breakthrough,3,1 +363,clerk,3,1 +364,clock,3,1 +365,commute,3,1 +366,dismiss,3,1 +367,duty,3,1 +368,entitlement,3,1 +369,executive,3,1 +370,fee,3,1 +371,furtherance,3,1 +372,grant,3,1 +373,grievance,3,1 +374,headquarter,3,1 +375,hierarchy,3,1 +376,injury,3,1 +377,insured,3,1 +378,labourer,3,1 +379,levy,3,1 +380,onus,3,1 +381,preferment,3,1 +382,reprimand,3,1 +383,retain,3,1 +384,retirement,3,1 +385,rig,3,1 +386,rookie,3,1 +387,uphold,3,1 +388,wage,3,1 +389,warhorse,3,1 +390,appease,3,1 +391,avenge,3,1 +392,berserk,3,1 +393,binge,3,1 +394,bitter,3,1 +395,blowout,3,1 +396,bustle,3,1 +397,cocky,3,1 +398,corny,3,1 +399,dismantle,3,1 +400,entrust,3,1 +401,gall,3,1 +402,grumpy,3,1 +403,lull,3,1 +404,nasty,3,1 +405,nefarious,3,1 +406,numb,3,1 +407,outsource,3,1 +408,plucky,3,1 +409,poised,3,1 +410,quell,3,1 +411,ravage,3,1 +412,retaliation,3,1 +413,ricochet,3,1 +414,rotten,3,1 +415,scrap,3,1 +416,swagger,3,1 +417,vengeance,3,1 +418,vengeful,3,1 +419,wicked,3,1 +420,avenue,3,1 +421,bland,3,1 +422,bolt,3,1 +423,cheerful,3,1 +424,clutch,3,1 +425,drill,3,1 +426,file,3,1 +427,flood,3,1 +428,frill,3,1 +429,gale,3,1 +430,gravel,3,1 +431,hail,3,1 +432,horn,3,1 +433,hose,3,1 +434,lawn,3,1 +435,livid,3,1 +436,monger,3,1 +437,mouldy,3,1 +438,nail,3,1 +439,pliers,3,1 +440,ripe,3,1 +441,screw,3,1 +442,screwdriver,3,1 +443,shovel,3,1 +444,signpost,3,1 +445,sneeze,3,1 +446,spleen,3,1 +447,vice,3,1 +448,weary,3,1 +449,windpipe,3,1 +450,belittle,3,1 +451,beware,3,1 +452,bleary,3,1 +453,bonehead,3,1 +454,chap,3,1 +455,coax,3,1 +456,deem,3,1 +457,dud,3,1 +458,eschew,3,1 +459,groggy,3,1 +460,midget,3,1 +461,navel,3,1 +462,nibble,3,1 +463,nonetheless,3,1 +464,preemptive,3,1 +465,quits,3,1 +466,savvy,3,1 +467,scatter,3,1 +468,sheepish,3,1 +469,snag,3,1 +470,squabble,3,1 +471,straddle,3,1 +472,sullen,3,1 +473,tweak,3,1 +474,twofold,3,1 +475,vow,3,1 +476,weakling,3,1 +477,whiny,3,1 +478,wit,3,1 +479,wrath,3,1 +480,aloof,3,1 +481,bargain,3,1 +482,bide,3,1 +483,clover,3,1 +484,countenance,3,1 +485,deceive,3,1 +486,doom,3,1 +487,fidget,3,1 +488,fled,3,1 +489,flog,3,1 +490,foil,3,1 +491,forgery,3,1 +492,frisk,3,1 +493,frisky,3,1 +494,hitherto,3,1 +495,huddled,3,1 +496,hullabaloo,3,1 +497,limber,3,1 +498,litter,3,1 +499,mingle,3,1 +500,outcry,3,1 +501,overthrow,3,1 +502,paw,3,1 +503,quarry,3,1 +504,ram,3,1 +505,seize,3,1 +506,shrewd,3,1 +507,tame,3,1 +508,treachery,3,1 +509,windfall,3,1 +510,akin,3,1 +511,allowance,3,1 +512,barley,3,1 +513,burden,3,1 +514,cellar,3,1 +515,chancy,3,1 +516,convey,3,1 +517,deject,3,1 +518,eavesdrop,3,1 +519,forbid,3,1 +520,foretell,3,1 +521,hapless,3,1 +522,hassle,3,1 +523,haste,3,1 +524,hazard,3,1 +525,heist,3,1 +526,henceforward,3,1 +527,hindsight,3,1 +528,hunch,3,1 +529,hush,3,1 +530,loose,3,1 +531,lure,3,1 +532,marvel,3,1 +533,poignant,3,1 +534,quarrel,3,1 +535,raffle,3,1 +536,resent,3,1 +537,stroll,3,1 +538,upend,3,1 +539,whence,3,1 +540,almond,3,1 +541,benchmark,3,1 +542,cadre,3,1 +543,cajole,3,1 +544,defer,3,1 +545,flummox,3,1 +546,grudge,3,1 +547,headlong,3,1 +548,jittery,3,1 +549,kale,3,1 +550,layman,3,1 +551,likewise,3,1 +552,maladroit,3,1 +553,meander,3,1 +554,moan,3,1 +555,moreover,3,1 +556,newfangled,3,1 +557,nigh,3,1 +558,otiose,3,1 +559,owing,3,1 +560,plainly,3,1 +561,slump,3,1 +562,staunch,3,1 +563,steadfast,3,1 +564,stoop,3,1 +565,thereby,3,1 +566,uncanny,3,1 +567,unfettered,3,1 +568,unviable,3,1 +569,winsome,3,1 +570,befuddle,3,1 +571,beg,3,1 +572,bicker,3,1 +573,candor,3,1 +574,chary,3,1 +575,crass,3,1 +576,crate,3,1 +577,endow,3,1 +578,grit,3,1 +579,harass,3,1 +580,harbinger,3,1 +581,hardship,3,1 +582,jolt,3,1 +583,jumble,3,1 +584,maudlin,3,1 +585,onslaught,3,1 +586,palliate,3,1 +587,scald,3,1 +588,scam,3,1 +589,scold,3,1 +590,slake,3,1 +591,slaughter,3,1 +592,slay,3,1 +593,spite,3,1 +594,subtle,3,1 +595,thereof,3,1 +596,unruffled,3,1 +597,unruly,3,1 +598,unscathed,3,1 +599,unscramble,3,1 +600,allegiance,3,1 +601,askew,3,1 +602,bare,3,1 +603,claw,3,1 +604,crow,3,1 +605,devoid,3,1 +606,dwindle,3,1 +607,feather,3,1 +608,flutter,3,1 +609,hobble,3,1 +610,jaw,3,1 +611,linger,3,1 +612,lunge,3,1 +613,nostrils,3,1 +614,nudge,3,1 +615,peck,3,1 +616,prickle,3,1 +617,recoil,3,1 +618,retort,3,1 +619,screech,3,1 +620,shrug,3,1 +621,sleeve,3,1 +622,slight,3,1 +623,sluggish,3,1 +624,slums,3,1 +625,sterner,3,1 +626,swoop,3,1 +627,testy,3,1 +628,throb,3,1 +629,yeast,3,1 +630,appoint,3,1 +631,ashamed,3,1 +632,beckon,3,1 +633,charm,3,1 +634,chasm,3,1 +635,creak,3,1 +636,decline,3,1 +637,despise,3,1 +638,eerie,3,1 +639,glare,3,1 +640,glint,3,1 +641,grimace,3,1 +642,lid,3,1 +643,malice,3,1 +644,menace,3,1 +645,restrain,3,1 +646,sag,3,1 +647,scowl,3,1 +648,shudder,3,1 +649,slither,3,1 +650,smirk,3,1 +651,sore,3,1 +652,taut,3,1 +653,thigh,3,1 +654,tousle,3,1 +655,trickle,3,1 +656,waist,3,1 +657,willowy,3,1 +658,writhe,3,1 +659,abreast,3,1 +660,alley,3,1 +661,asunder,3,1 +662,avert,3,1 +663,brow,3,1 +664,coerce,3,1 +665,dam,3,1 +666,dank,3,1 +667,doze,3,1 +668,dumpling,3,1 +669,estate,3,1 +670,forthcoming,3,1 +671,forthright,3,1 +672,gist,3,1 +673,goose bumps,3,1 +674,is about time,3,1 +675,juggernaut,3,1 +676,jutting,3,1 +677,ludicrous,3,1 +678,marrow,3,1 +679,muster,3,1 +680,oblige,3,1 +681,redeem,3,1 +682,shuffle,3,1 +683,skid,3,1 +684,teem,3,1 +685,time and again,3,1 +686,unbeknownst,3,1 +687,whim,3,1 +688,wobble,3,1 +689,anyhow,3,1 +690,avow,3,1 +691,beget,3,1 +692,bond,3,1 +693,bouncer,3,1 +694,cleave,3,1 +695,deadbeat,3,1 +696,demand,3,1 +697,dismal,3,1 +698,dough,3,1 +699,extol,3,1 +700,gargantuan,3,1 +701,gash,3,1 +702,heir,3,1 +703,hindrance,3,1 +704,inasmuch,3,1 +705,infamous,3,1 +706,inflame,3,1 +707,inquire,3,1 +708,jew,3,1 +709,mild,3,1 +710,murk,3,1 +711,offspring,3,1 +712,portray,3,1 +713,prattle,3,1 +714,raze,3,1 +715,relate,3,1 +716,rupture,3,1 +717,smooth,3,1 +718,taint,3,1 +719,bind,3,1 +720,blueprint,3,1 +721,bolster,3,1 +722,bound,3,1 +723,coherent,3,1 +724,dingy,3,1 +725,dodge,3,1 +726,doodle,3,1 +727,enforce,3,1 +728,engage,3,1 +729,extent,3,1 +730,foresee,3,1 +731,fortnight,3,1 +732,glimpse,3,1 +733,herd,3,1 +734,jargon,3,1 +735,overall,3,1 +736,painstaking,3,1 +737,reckon,3,1 +738,rewind,3,1 +739,sheer,3,1 +740,silly,3,1 +741,squeamish,3,1 +742,steady,3,1 +743,strike,3,1 +744,surmise,3,1 +745,thrill,3,1 +746,thrust,3,1 +747,truce,3,1 +748,ungainly,3,1 +749,attorney,3,1 +750,averse,3,1 +751,boar,3,1 +752,buff,3,1 +753,bundle,3,1 +754,crook,3,1 +755,cub,3,1 +756,disown,3,1 +757,distress,3,1 +758,encompass,3,1 +759,feat,3,1 +760,gory,3,1 +761,kindle,3,1 +762,leap,3,1 +763,let alone,3,1 +764,nuisance,3,1 +765,odd,3,1 +766,override,3,1 +767,peep,3,1 +768,placeholder,3,1 +769,preach,3,1 +770,regardless,3,1 +771,sap,3,1 +772,sneak,3,1 +773,stream,3,1 +774,sue,3,1 +775,thwart,3,1 +776,tuition,3,1 +777,vacuum,3,1 +778,warp,3,1 +779,ancillary,3,1 +780,bewilder,3,1 +781,blurt,3,1 +782,canned,3,1 +783,clout,3,1 +784,cutlery,3,1 +785,dire,3,1 +786,draft,3,1 +787,duvet,3,1 +788,entice,3,1 +789,fortitude,3,1 +790,hitch,3,1 +791,hustle,3,1 +792,jeopardize,3,1 +793,lush,3,1 +794,mesmerize,3,1 +795,naive,3,1 +796,outcast,3,1 +797,patch,3,1 +798,peddle,3,1 +799,poke,3,1 +800,puny,3,1 +801,roach,3,1 +802,shaky,3,1 +803,skimpy,3,1 +804,sly,3,1 +805,soothe,3,1 +806,spate,3,1 +807,steam,3,1 +808,tickle,3,1 +809,aubergine,3,1 +810,beef,3,1 +811,bleach,3,1 +812,bowl,3,1 +813,bulky,3,1 +814,courgette,3,1 +815,crunchy,3,1 +816,cucumber,3,1 +817,cunning,3,1 +818,farmhouse,3,1 +819,flake,3,1 +820,frankfurter,3,1 +821,ham,3,1 +822,huffy,3,1 +823,kettle,3,1 +824,loaf,3,1 +825,mighty,3,1 +826,mug,3,1 +827,pea,3,1 +828,peanut,3,1 +829,pepper,3,1 +830,pliable,3,1 +831,pot,3,1 +832,reckless,3,1 +833,rinse,3,1 +834,roll,3,1 +835,sponge,3,1 +836,stove,3,1 +837,tumbler,3,1 +838,wholewheat,3,1 +839,blotch,3,1 +840,chute,3,1 +841,conceal,3,1 +842,dare,3,1 +843,dim,3,1 +844,disposal,3,1 +845,dope,3,1 +846,exertion,3,1 +847,exile,3,1 +848,expend,3,1 +849,fizzy,3,1 +850,gingerly,3,1 +851,grim,3,1 +852,hood,3,1 +853,insurgent,3,1 +854,jostle,3,1 +855,keepsake,3,1 +856,laundry,3,1 +857,lukewarm,3,1 +858,mend,3,1 +859,outburst,3,1 +860,outer,3,1 +861,peel,3,1 +862,puffy,3,1 +863,scrape,3,1 +864,sharp,3,1 +865,slack,3,1 +866,tray,3,1 +867,withdraw,3,1 +868,withhold,3,1 +869,abduct,3,1 +870,amuse,3,1 +871,bid,3,1 +872,blatant,3,1 +873,bluster,3,1 +874,booze,3,1 +875,cabbage,3,1 +876,cauliflower,3,1 +877,cherish,3,1 +878,chickpeas,3,1 +879,disclose,3,1 +880,feeble,3,1 +881,freckle,3,1 +882,get lost,3,1 +883,graze,3,1 +884,hind,3,1 +885,keen,3,1 +886,ladle,3,1 +887,lift,3,1 +888,outage,3,1 +889,padlock,3,1 +890,pester,3,1 +891,poke around,3,1 +892,scorch,3,1 +893,slur,3,1 +894,squash,3,1 +895,tissue,3,1 +896,tummy,3,1 +897,tycoon,3,1 +898,verge,3,1 +899,astray,3,1 +900,brew,3,1 +901,capsize,3,1 +902,conceive,3,1 +903,concoction,3,1 +904,cuisine,3,1 +905,dazzle,3,1 +906,deplete,3,1 +907,disgruntled,3,1 +908,embed,3,1 +909,farewell,3,1 +910,flock,3,1 +911,folk,3,1 +912,forgo,3,1 +913,haughty,3,1 +914,interplay,3,1 +915,intrude,3,1 +916,jest,3,1 +917,mock,3,1 +918,onset,3,1 +919,poise,3,1 +920,respite,3,1 +921,savor,3,1 +922,shabby,3,1 +923,smug,3,1 +924,summon,3,1 +925,thrift,3,1 +926,typo,3,1 +927,upheaval,3,1 +928,warfare,3,1 +929,beat,3,1 +930,bother,3,1 +931,bumblebee,3,1 +932,colader,3,1 +933,discombobulate,3,1 +934,disease,3,1 +935,fleeting,3,1 +936,fluffy,3,1 +937,hammer,3,1 +938,homesick,3,1 +939,issue,3,1 +940,janky,3,1 +941,lame,3,1 +942,lullaby,3,1 +943,mill,3,1 +944,mood,3,1 +945,narrow,3,1 +946,rise,3,1 +947,scissor,3,1 +948,silky,3,1 +949,smell,3,1 +950,spine,3,1 +951,stale,3,1 +952,stink,3,1 +953,swarm,3,1 +954,tapestry,3,1 +955,unbearable,3,1 +956,unholy,3,1 +957,unspeakable,3,1 +958,wriggle,3,1 +959,allure,3,1 +960,audit,3,1 +961,bequeath,3,1 +962,bestow,3,1 +963,bloodlust,3,1 +964,bristle,3,1 +965,conceit,3,1 +966,deranged,3,1 +967,engender,3,1 +968,engulf,3,1 +969,firm,3,1 +970,fray,3,1 +971,gaudy,3,1 +972,hubris,3,1 +973,hurl,3,1 +974,leak,3,1 +975,lowly,3,1 +976,parsley,3,1 +977,plunder,3,1 +978,pour,3,1 +979,seep,3,1 +980,shore,3,1 +981,shun,3,1 +982,siege,3,1 +983,squander,3,1 +984,strife,3,1 +985,tattle,3,1 +986,toil,3,1 +987,unbridled,3,1 +988,wrought,3,1 +989,aghast,3,1 +990,amiable,3,1 +991,banter,3,1 +992,bark,3,1 +993,bootlicker,3,1 +994,cozy,3,1 +995,feign,3,1 +996,imp,3,1 +997,knack,3,1 +998,laxity,3,1 +999,overactive,3,1 +1000,paucity,3,1 +1001,paunch,3,1 +1002,plod,3,1 +1003,purse,3,1 +1004,rust,3,1 +1005,slouch,3,1 +1006,tease,3,1 +1007,tin,3,1 +1008,woo,3,1 +1009,appraisal,3,1 +1010,burglary,3,1 +1011,chronicle,3,1 +1012,drift,3,1 +1013,fare,3,1 +1014,foe,3,1 +1015,gainsay,3,1 +1016,gauge,3,1 +1017,gridlock,3,1 +1018,hatch,3,1 +1019,hijack,3,1 +1020,hinge,3,1 +1021,matter of fact,3,1 +1022,mishap,3,1 +1023,plight,3,1 +1024,ponder,3,1 +1025,remedy,3,1 +1026,rife,3,1 +1027,seethe,3,1 +1028,tear,3,1 +1029,aid,3,1 +1030,amid,3,1 +1031,belie,3,1 +1032,blanch,3,1 +1033,boulder,3,1 +1034,carve,3,1 +1035,fable,3,1 +1036,gaze,3,1 +1037,glee,3,1 +1038,irate,3,1 +1039,muddle,3,1 +1040,outmatch,3,1 +1041,peevish,3,1 +1042,rape,3,1 +1043,sage,3,1 +1044,sangfroid,3,1 +1045,scorn,3,1 +1046,stickler,3,1 +1047,strain,3,1 +1048,wraith,3,1 +1049,befall,3,1 +1050,bemoan,3,1 +1051,bogus,3,1 +1052,budge,3,1 +1053,comb,3,1 +1054,flinch,3,1 +1055,fret,3,1 +1056,grovel,3,1 +1057,haul,3,1 +1058,janitor,3,1 +1059,ominous,3,1 +1060,peg,3,1 +1061,physician,3,1 +1062,pique,3,1 +1063,quip,3,1 +1064,raid,3,1 +1065,rally,3,1 +1066,sinew,3,1 +1067,slog,3,1 +1068,stomp,3,1 +1069,acquiesce,3,1 +1070,backbone,3,1 +1071,bravado,3,1 +1072,brood,3,1 +1073,connive,3,1 +1074,dean,3,1 +1075,fiddle,3,1 +1076,flank,3,1 +1077,impasse,3,1 +1078,maverick,3,1 +1079,outlook,3,1 +1080,pin,3,1 +1081,privy,3,1 +1082,punk,3,1 +1083,purport,3,1 +1084,resign,3,1 +1085,shortcoming,3,1 +1086,solace,3,1 +1087,swoon,3,1 +1088,trailblazer,3,1 +1089,cater,3,1 +1090,chastise,3,1 +1091,drench,3,1 +1092,duly,3,1 +1093,giddy,3,1 +1094,glide,3,1 +1095,haggle,3,1 +1096,innuendo,3,1 +1097,inveterate,3,1 +1098,morose,3,1 +1099,slander,3,1 +1100,slip,3,1 +1101,snub,3,1 +1102,stow,3,1 +1103,sulk,3,1 +1104,swathe,3,1 +1105,throttle,3,1 +1106,weld,3,1 +1107,wherewithal,3,1 +1108,wring,3,1 +1109,belated,3,1 +1110,blacken,3,1 +1111,booth,3,1 +1112,chagrin,3,1 +1113,cling,3,1 +1114,curt,3,1 +1115,disparage,3,1 +1116,dispirited,3,1 +1117,flippant,3,1 +1118,fond,3,1 +1119,gust,3,1 +1120,husk,3,1 +1121,pouch,3,1 +1122,reliable,3,1 +1123,scrub,3,1 +1124,secretive,3,1 +1125,tack,3,1 +1126,thump,3,1 +1127,wheedle,3,1 +1128,wretch,3,1 +1129,ablaze,3,1 +1130,apricot,3,1 +1131,bystander,3,1 +1132,distraught,3,1 +1133,engrossed,3,1 +1134,flare,3,1 +1135,hilt,3,1 +1136,meddle,3,1 +1137,memento,3,1 +1138,overcast,3,1 +1139,perk,3,1 +1140,pretence,3,1 +1141,prone,3,1 +1142,pry,3,1 +1143,refrain,3,1 +1144,shambles,3,1 +1145,squat,3,1 +1146,sultry,3,1 +1147,tipsy,3,1 +1148,twine,3,1 +1149,allegedly,3,1 +1150,botch,3,1 +1151,chink,3,1 +1152,coalesce,3,1 +1153,conduit,3,1 +1154,conjure,3,1 +1155,daredevil,3,1 +1156,detour,3,1 +1157,dispute,3,1 +1158,enchant,3,1 +1159,golly,3,1 +1160,ledger,3,1 +1161,mortgage,3,1 +1162,picky,3,1 +1163,poll,3,1 +1164,topping,3,1 +1165,tug,3,1 +1166,undaunted,3,1 +1167,valiant,3,1 +1168,yelp,3,1 +1169,butler,3,1 +1170,callous,3,1 +1171,crib,3,1 +1172,disposable,3,1 +1173,enmity,3,1 +1174,equitable,3,1 +1175,hazel,3,1 +1176,hectic,3,1 +1177,impromptu,3,1 +1178,mettle,3,1 +1179,ooze,3,1 +1180,overhaul,3,1 +1181,pamphlet,3,1 +1182,proclivity,3,1 +1183,puff,3,1 +1184,purveyor,3,1 +1185,purview,3,1 +1186,quirk,3,1 +1187,rim,3,1 +1188,wunderkind,3,1 +1189,amend,3,1 +1190,ascribe,3,1 +1191,bartender,3,1 +1192,breed,3,1 +1193,crop,3,1 +1194,envisage,3,1 +1195,fetch,3,1 +1196,flaw,3,1 +1197,flea,3,1 +1198,hallow,3,1 +1199,hollow,3,1 +1200,humdrum,3,1 +1201,mayhem,3,1 +1202,misgiving,3,1 +1203,pawn,3,1 +1204,pinpoint,3,1 +1205,replete,3,1 +1206,withstand,3,1 +1207,woe,3,1 +1208,yolk,3,1 +1209,bedevil,3,1 +1210,bout,3,1 +1211,clearance,3,1 +1212,debunk,3,1 +1213,facade,3,1 +1214,fathom,3,1 +1215,fringe,3,1 +1216,fuss,3,1 +1217,hare,3,1 +1218,heinous,3,1 +1219,imbue,3,1 +1220,impinge,3,1 +1221,murky,3,1 +1222,notwithstanding,3,1 +1223,overdue,3,1 +1224,pundit,3,1 +1225,redress,3,1 +1226,snatch,3,1 +1227,supple,3,1 +1228,trifle,3,1 +1229,apprehend,3,1 +1230,beyond,3,1 +1231,bottom line,3,1 +1232,bundle up,3,1 +1233,by chance,3,1 +1234,catch up,3,1 +1235,chief,3,1 +1236,drop by,3,1 +1237,easy does it,3,1 +1238,gullible,3,1 +1239,lean towards,3,1 +1240,mark my words,3,1 +1241,mischievous,3,1 +1242,on a roll,3,1 +1243,repartee,3,1 +1244,rule out,3,1 +1245,set off,3,1 +1246,swab,3,1 +1247,ward off,3,1 +1248,whistleblower,3,1 +1249,assuage,3,1 +1250,clot,3,1 +1251,enlist,3,1 +1252,epitome,3,1 +1253,for a song,3,1 +1254,go with the flow,3,1 +1255,knead,3,1 +1256,make do,3,1 +1257,neck and neck,3,1 +1258,next of kin,3,1 +1259,nosey,3,1 +1260,plummet,3,1 +1261,prickly,3,1 +1262,reassure,3,1 +1263,resilient,3,1 +1264,seaweed,3,1 +1265,so to speak,3,1 +1266,under fire,3,1 +1267,vinegar,3,1 +1268,zip it,3,1 +1269,cry me a river,3,1 +1270,deadpan,3,1 +1271,double take,3,1 +1272,flabbergast,3,1 +1273,flush,3,1 +1274,hang up,3,1 +1275,mend fences,3,1 +1276,nick of time,3,1 +1277,over and out,3,1 +1278,parlor,3,1 +1279,patronize,3,1 +1280,plum,3,1 +1281,reek,3,1 +1282,sea change,3,1 +1283,sweatshirt,3,1 +1284,touchstone,3,1 +1285,up to the mark,3,1 +1286,vexed,3,1 +1287,wind up,3,1 +1288,wrench,3,1 +1289,besmirch,3,1 +1290,fan,3,1 +1291,feckless,3,1 +1292,forlorn,3,1 +1293,gristle,3,1 +1294,heel,3,1 +1295,holdover,3,1 +1296,impugn,3,1 +1297,jeer,3,1 +1298,petty,3,1 +1299,pun,3,1 +1300,rabble,3,1 +1301,sash,3,1 +1302,shrill,3,1 +1303,sissy,3,1 +1304,slant,3,1 +1305,sour,3,1 +1306,unravel,3,1 +1307,vagrant,3,1 +1308,wool,3,1 +1309,afloat,3,1 +1310,beet,3,1 +1311,clumsy,3,1 +1312,collateral,3,1 +1313,conundrum,3,1 +1314,corroborate,3,1 +1315,crutch,3,1 +1316,cudgel,3,1 +1317,disabuse,3,1 +1318,floss,3,1 +1319,hearsay,3,1 +1320,knit,3,1 +1321,learn by rote,3,1 +1322,lookup,3,1 +1323,lore,3,1 +1324,mimicry,3,1 +1325,rumple,3,1 +1326,serendipity,3,1 +1327,vie,3,1 +1328,vindicate,3,1 +1329,blend,3,1 +1330,broth,3,1 +1331,crumble,3,1 +1332,errand,3,1 +1333,gimmick,3,1 +1334,glean,3,1 +1335,glib,3,1 +1336,ironclad,3,1 +1337,maize,3,1 +1338,measly,3,1 +1339,mouthful,3,1 +1340,nostrum,3,1 +1341,pristine,3,1 +1342,pulp,3,1 +1343,shallow,3,1 +1344,shear,3,1 +1345,tenure,3,1 +1346,torn,3,1 +1347,twig,3,1 +1348,velvet,3,1 +2048,angoscia,3,2 +2049,amato,3,2 +2050,purificare,3,2 +2051,mantello,3,2 +2052,alba,3,2 +2053,azione / atto,3,2 +2054,dimorare,3,2 +2055,severo,3,2 +2056,in effetti,3,2 +2057,scagnozzo,3,2 +2058,perizoma,3,2 +2059,pieta',3,2 +2060,prendere parte,3,2 +2061,sporgere,3,2 +2062,attenuazione,3,2 +2063,snello,3,2 +2064,tristezza,3,2 +2065,risparmiare,3,2 +2066,germogliare,3,2 +2067,rigido,3,2 +2068,abbronzatura,3,2 +2069,spinosi,3,2 +2070,pertanto,3,2 +2071,instabile,3,2 +2072,vanitoso,3,2 +2073,lamento,3,2 +2074,scemare,3,2 +2075,tessere,3,2 +2076,culto,3,2 +2077,abbondare,3,2 +2078,birrificio,3,2 +2079,bestiame,3,2 +2080,fattibile,3,2 +2081,stranamente,3,2 +2082,competenza,3,2 +2083,colpetto,3,2 +2084,lanciato,3,2 +2085,borbottio,3,2 +2086,avido,3,2 +2087,alone,3,2 +2088,sfruttato,3,2 +2089,creazione,3,2 +2090,in lizza,3,2 +2091,ribalta,3,2 +2092,prestito,3,2 +2093,tovagliolo,3,2 +2094,fondamentale,3,2 +2095,compensato,3,2 +2096,grezzo,3,2 +2097,ricetta,3,2 +2098,sella,3,2 +2099,viscido,3,2 +2100,spargere,3,2 +2101,barcollare,3,2 +2102,randagio,3,2 +2103,scongelare,3,2 +2104,approfondito,3,2 +2105,rovesciare,3,2 +2106,subbuglio,3,2 +2107,sottostante,3,2 +2108,pregiudizio,3,2 +2109,vantarsi,3,2 +2110,ampiezza,3,2 +2111,seppellire,3,2 +2112,affrontare,3,2 +2113,impedire,3,2 +2114,divinita',3,2 +2115,argine,3,2 +2116,traghetto,3,2 +2117,mania,3,2 +2118,indumento,3,2 +2119,sofferenza,3,2 +2120,fazzolletto,3,2 +2121,fare attenzione,3,2 +2122,affinare,3,2 +2123,gobba,3,2 +2124,drogato,3,2 +2125,detestare,3,2 +2126,docilmente,3,2 +2127,tagliaerba,3,2 +2128,ignaro,3,2 +2129,enigma,3,2 +2130,mensola,3,2 +2131,suolo,3,2 +2132,robusto,3,2 +2133,capriccio,3,2 +2134,banale,3,2 +2135,garantire,3,2 +2136,rendimento,3,2 +2137,corridoio,3,2 +2138,giustamente,3,2 +2139,soggezione,3,2 +2140,schietto,3,2 +2141,strisce pedonali,3,2 +2142,scadente,3,2 +2143,ingombrante,3,2 +2144,ostinato,3,2 +2145,sfrattare,3,2 +2146,moda,3,2 +2147,fragile,3,2 +2148,andatura,3,2 +2149,scommettere,3,2 +2150,burbero,3,2 +2151,ostacolare,3,2 +2152,scompiglio,3,2 +2153,vasetto,3,2 +2154,ritardatario,3,2 +2155,sfumatura,3,2 +2156,odioso,3,2 +2157,spodestare,3,2 +2158,sconto,3,2 +2159,fama,3,2 +2160,ribelle,3,2 +2161,elegante,3,2 +2162,manovrare,3,2 +2163,mescolare,3,2 +2164,infallibile,3,2 +2165,influenzare,3,2 +2166,prosperare,3,2 +2167,rispettare,3,2 +2168,andare storto,3,2 +2169,scoraggiarsi,3,2 +2170,malconcio,3,2 +2171,sinonimo,3,2 +2172,risatina,3,2 +2173,stringere,3,2 +2174,disordine,3,2 +2175,stipare,3,2 +2176,bramare,3,2 +2177,intimidire,3,2 +2178,elaborare,3,2 +2179,fosso,3,2 +2180,allibito,3,2 +2181,tramonto,3,2 +2182,iscriversi,3,2 +2183,esitare,3,2 +2184,incerto,3,2 +2185,bottino,3,2 +2186,marmo,3,2 +2187,compagno,3,2 +2188,tono,3,2 +2189,appellarsi,3,2 +2190,spietato,3,2 +2191,scarso,3,2 +2192,peccato,3,2 +2193,ripido,3,2 +2194,sforzarsi,3,2 +2195,contrazione,3,2 +2196,ondeggiare,3,2 +2197,sebbene,3,2 +2198,sfrenato,3,2 +2199,per conto,3,2 +2200,corrompere,3,2 +2201,gioco da ragazzi,3,2 +2202,rivendicazione,3,2 +2203,frenare,3,2 +2204,dilettarsi,3,2 +2205,idem,3,2 +2206,scorie,3,2 +2207,siccita',3,2 +2208,fatica,3,2 +2209,comportare,3,2 +2210,schiuma,3,2 +2211,sembianze,3,2 +2212,foschia,3,2 +2213,quindi,3,2 +2214,singhiozzo,3,2 +2215,ovviamente,3,2 +2216,stagno,3,2 +2217,retro,3,2 +2218,provare,3,2 +2219,accidia,3,2 +2220,scheggia,3,2 +2221,gonfiare,3,2 +2222,trainare,3,2 +2223,completamente,3,2 +2224,cera,3,2 +2225,grano,3,2 +2226,astuto,3,2 +2227,fondale,3,2 +2228,palude,3,2 +2229,insolente,3,2 +2230,intasare,3,2 +2231,arricciare,3,2 +2232,costernato,3,2 +2233,rafforzare,3,2 +2234,valorizzare,3,2 +2235,inverosimile,3,2 +2236,favorire,3,2 +2237,appariscente,3,2 +2238,azzardato,3,2 +2239,seccante,3,2 +2240,sontuoso,3,2 +2241,guinzaglio,3,2 +2242,aggredito,3,2 +2243,ennuplicato,3,2 +2244,famigerato,3,2 +2245,brevetto,3,2 +2246,fastidioso,3,2 +2247,divagare,3,2 +2248,distorcere,3,2 +2249,pendenza,3,2 +2250,sciatto,3,2 +2251,spaventare,3,2 +2252,sarto,3,2 +2253,armeggiare,3,2 +2254,diffidente,3,2 +2255,sorgente,3,2 +2256,sarcastico,3,2 +2257,riunione,3,2 +2258,argilla,3,2 +2259,fessura,3,2 +2260,storpio,3,2 +2261,investigare,3,2 +2262,atteggiamento,3,2 +2263,travestimento,3,2 +2264,volubile,3,2 +2265,pagliaio,3,2 +2266,ronzio,3,2 +2267,responsabile,3,2 +2268,oziare,3,2 +2269,incombente,3,2 +2270,agile,3,2 +2271,angolino,3,2 +2272,prioritario,3,2 +2273,propensione,3,2 +2274,pietoso,3,2 +2275,querela,3,2 +2276,stratagemma,3,2 +2277,rovistare,3,2 +2278,fruscio,3,2 +2279,cucito,3,2 +2280,spronare,3,2 +2281,soffocare,3,2 +2282,testardo,3,2 +2283,truffa,3,2 +2284,leccornia,3,2 +2285,benessere,3,2 +2286,posizione,3,2 +2287,cauzione,3,2 +2288,cespuglio,3,2 +2289,grossolano,3,2 +2290,rame,3,2 +2291,gingillarsi,3,2 +2292,suscitare,3,2 +2293,colpo di fortuna,3,2 +2294,lucido,3,2 +2295,arrestare,3,2 +2296,accumulare,3,2 +2297,esempio,3,2 +2298,chiavistello,3,2 +2299,appostarsi,3,2 +2300,tuttavia,3,2 +2301,nutrire,3,2 +2302,insidia,3,2 +2303,abilita',3,2 +2304,ricaduta,3,2 +2305,fantasticheria,3,2 +2306,ristretto,3,2 +2307,scremare,3,2 +2308,scalinata,3,2 +2309,allettante,3,2 +2310,buttare,3,2 +2311,vizioso,3,2 +2312,strambo,3,2 +2313,reparto,3,2 +2314,sentore,3,2 +2315,appassire,3,2 +2316,ruga,3,2 +2317,cenere,3,2 +2318,coperta,3,2 +2319,caldaia,3,2 +2320,scopa,3,2 +2321,armadietto,3,2 +2322,lampadario,3,2 +2323,stoviglie,3,2 +2324,cuscino,3,2 +2325,scolapiatti,3,2 +2326,maniglia,3,2 +2327,colomba,3,2 +2328,cassetto,3,2 +2329,bracie,3,2 +2330,poggiapiedi,3,2 +2331,brontolare,3,2 +2332,lancetta,3,2 +2333,serratura,3,2 +2334,soprammobile,3,2 +2335,forno,3,2 +2336,dispensa,3,2 +2337,termosifone,3,2 +2338,zaino,3,2 +2339,persiana,3,2 +2340,lavandino,3,2 +2341,pantofola,3,2 +2342,presa,3,2 +2343,rubinetto,3,2 +2344,piastrella,3,2 +2345,lavatoio,3,2 +2346,stufa,3,2 +2347,piazzale,3,2 +2348,carriola,3,2 +2349,faggio,3,2 +2350,betulla,3,2 +2351,merlo,3,2 +2352,carbone,3,2 +2353,rampicante,3,2 +2354,recinzione,3,2 +2355,amaca,3,2 +2356,raccolto,3,2 +2357,fieno,3,2 +2358,siepe,3,2 +2359,zappare,3,2 +2360,calabrone,3,2 +2361,arto,3,2 +2362,letame,3,2 +2363,fango,3,2 +2364,ciottolo,3,2 +2365,piccone,3,2 +2366,arare,3,2 +2367,portico,3,2 +2368,potare,3,2 +2369,rastrello,3,2 +2370,altalena,3,2 +2371,arbusto,3,2 +2372,seminare,3,2 +2373,vanga,3,2 +2374,paglia,3,2 +2375,terrazza,3,2 +2376,davanzale,3,2 +2377,anticipo,3,2 +2378,arduo,3,2 +2379,arrivo,3,2 +2380,bagaglio,3,2 +2381,prenotare,3,2 +2382,ufficio,3,2 +2383,garzone,3,2 +2384,partire,3,2 +2385,spedizione,3,2 +2386,approvare,3,2 +2387,scadenza,3,2 +2388,estero,3,2 +2389,porto,3,2 +2390,inebriante,3,2 +2391,autostop,3,2 +2392,rinvigorire,3,2 +2393,atterraggio,3,2 +2394,verso l'esterno,3,2 +2395,oltremare,3,2 +2396,pedone,3,2 +2397,rimandare,3,2 +2398,navigare,3,2 +2399,marciapiede,3,2 +2400,comprovare,3,2 +2401,decollo,3,2 +2402,casello,3,2 +2403,pedaggio,3,2 +2404,travagliato,3,2 +2405,impresa,3,2 +2406,parabrezza,3,2 +2407,aggiornamento,3,2 +2408,apprendista,3,2 +2409,svolta,3,2 +2410,commesso,3,2 +2411,timbrare,3,2 +2412,pendolare,3,2 +2413,licenziare,3,2 +2414,dovere,3,2 +2415,diritto,3,2 +2416,dirigente,3,2 +2417,tassa,3,2 +2418,avanzamento,3,2 +2419,concedere,3,2 +2420,reclamo,3,2 +2421,sede,3,2 +2422,gerarchia,3,2 +2423,infortunio,3,2 +2424,assicurato,3,2 +2425,operaio,3,2 +2426,imposta,3,2 +2427,onere,3,2 +2428,promozione,3,2 +2429,ammonizione,3,2 +2430,conservare,3,2 +2431,pensione,3,2 +2432,impianto,3,2 +2433,novellino,3,2 +2434,sostenere,3,2 +2435,stipendio,3,2 +2436,veterano,3,2 +2437,placare,3,2 +2438,vendicare,3,2 +2439,impazzito,3,2 +2440,abbuffata,3,2 +2441,amaro,3,2 +2442,scoppio,3,2 +2443,trambusto,3,2 +2444,presuntuoso,3,2 +2445,risaputo,3,2 +2446,smantellare,3,2 +2447,affidare,3,2 +2448,irritare,3,2 +2449,scontroso,3,2 +2450,pausa,3,2 +2451,disgustoso,3,2 +2452,nefasto,3,2 +2453,insensibile,3,2 +2454,esternalizzare,3,2 +2455,impavido,3,2 +2456,composto,3,2 +2457,reprimere,3,2 +2458,devastare,3,2 +2459,ritorsione,3,2 +2460,rimbalzare,3,2 +2461,marcio,3,2 +2462,rottame,3,2 +2463,spavalderia,3,2 +2464,rivalsa,3,2 +2465,vendicativo,3,2 +2466,malvagio,3,2 +2467,viale,3,2 +2468,insipido,3,2 +2469,bullone,3,2 +2470,allegro,3,2 +2471,frizione,3,2 +2472,trapano,3,2 +2473,lima,3,2 +2474,alluvione,3,2 +2475,fronzolo,3,2 +2476,burrasca,3,2 +2477,ghiaia,3,2 +2478,grandine,3,2 +2479,clacson,3,2 +2480,canna,3,2 +2481,prato,3,2 +2482,furibondo,3,2 +2483,commerciante,3,2 +2484,ammuffito,3,2 +2485,chiodo,3,2 +2486,pinza,3,2 +2487,maturo,3,2 +2488,vite,3,2 +2489,cacciavite,3,2 +2490,pala,3,2 +2491,cartello stradale,3,2 +2492,starnutire,3,2 +2493,milza,3,2 +2494,morsa,3,2 +2495,esausto,3,2 +2496,trachea,3,2 +2497,sminuire,3,2 +2498,diffidare,3,2 +2499,annebbiato,3,2 +2500,tonto,3,2 +2501,tizio,3,2 +2502,blandire,3,2 +2503,ritenere,3,2 +2504,difettoso,3,2 +2505,rifuggire,3,2 +2506,intontito,3,2 +2507,nanerottolo,3,2 +2508,ombelico,3,2 +2509,sgranocchiare,3,2 +2510,ciononostante,3,2 +2511,preventivo,3,2 +2512,pari,3,2 +2513,esperto,3,2 +2514,sparpagliarsi,3,2 +2515,imbarazzato,3,2 +2516,imprevisto,3,2 +2517,battibecco,3,2 +2518,cavalcioni,3,2 +2519,imbronciato,3,2 +2520,pizzicare,3,2 +2521,duplice,3,2 +2522,giuramento,3,2 +2523,smidollato,3,2 +2524,piagnucoloso,3,2 +2525,arguzia,3,2 +2526,collera,3,2 +2527,disinteressato,3,2 +2528,accordo,3,2 +2529,attendere,3,2 +2530,trifoglio,3,2 +2531,espressione,3,2 +2532,ingannare,3,2 +2533,sventura,3,2 +2534,agitarsi,3,2 +2535,fuggire,3,2 +2536,frustare,3,2 +2537,sventare,3,2 +2538,falsificazione,3,2 +2539,perquisire,3,2 +2540,vivace,3,2 +2541,finora,3,2 +2542,rannicchiato,3,2 +2543,baccano,3,2 +2544,snodato,3,2 +2545,rifiuti,3,2 +2546,socializzare,3,2 +2547,protesta,3,2 +2548,deporre,3,2 +2549,zampa,3,2 +2550,cava,3,2 +2551,ariete,3,2 +2552,sequestrare,3,2 +2553,scaltro,3,2 +2554,domare,3,2 +2555,tradimento,3,2 +2556,imprevisti,3,2 +2557,simile,3,2 +2558,indennita',3,2 +2559,orzo,3,2 +2560,fardello,3,2 +2561,cantina,3,2 +2562,rischioso,3,2 +2563,trasmettere,3,2 +2564,demoralizzare,3,2 +2565,origliare,3,2 +2566,proibire,3,2 +2567,predire,3,2 +2568,sfortunato,3,2 +2569,scocciatura,3,2 +2570,fretta,3,2 +2571,rischio,3,2 +2572,rapina,3,2 +2573,d'ora in avanti,3,2 +2574,senno di poi,3,2 +2575,impressione,3,2 +2576,silenzio,3,2 +2577,sciolto,3,2 +2578,adescare,3,2 +2579,meraviglia,3,2 +2580,commovente,3,2 +2581,litigare,3,2 +2582,lotteria,3,2 +2583,risentirsi,3,2 +2584,passeggiata,3,2 +2585,capovolgere,3,2 +2586,da dove,3,2 +2587,mandorla,3,2 +2588,riferimento,3,2 +2589,squadra,3,2 +2590,persuadere,3,2 +2591,differire,3,2 +2592,sconcertare,3,2 +2593,rancore,3,2 +2594,a capofitto,3,2 +2595,agitato,3,2 +2596,verza,3,2 +2597,laico,3,2 +2598,similmente,3,2 +2599,maldestro,3,2 +2600,girovagare,3,2 +2601,gemere,3,2 +2602,oltretutto,3,2 +2603,moderno,3,2 +2604,imminente,3,2 +2605,superfluo,3,2 +2606,dovuto,3,2 +2607,chiaramente,3,2 +2608,crollo,3,2 +2609,fedele,3,2 +2610,deciso,3,2 +2611,chinarsi,3,2 +2612,in tal modo,3,2 +2613,inquietante,3,2 +2614,incondizionato,3,2 +2615,impraticabile,3,2 +2616,seducente,3,2 +2617,confondere,3,2 +2618,implorare,3,2 +2619,bisticciare,3,2 +2620,candore,3,2 +2621,cauto,3,2 +2622,volgare,3,2 +2623,cassa,3,2 +2624,dotare,3,2 +2625,grinta,3,2 +2626,molestare,3,2 +2627,precursore,3,2 +2628,disagio,3,2 +2629,scossa,3,2 +2630,guazzabuglio,3,2 +2631,sentimentale,3,2 +2632,assalto,3,2 +2633,mitigare,3,2 +2634,scottare,3,2 +2635,imbroglio,3,2 +2636,rimproverare,3,2 +2637,appagare,3,2 +2638,massacro,3,2 +2639,uccidere,3,2 +2640,dispetto,3,2 +2641,sottile,3,2 +2642,dello stesso,3,2 +2643,imperturbabile,3,2 +2644,indisciplinato,3,2 +2645,illeso,3,2 +2646,decodificare,3,2 +2647,fedelta',3,2 +2648,sbilenco,3,2 +2649,spoglio,3,2 +2650,artiglio,3,2 +2651,corvo,3,2 +2652,privo,3,2 +2653,ridursi,3,2 +2654,piuma,3,2 +2655,svolazzare,3,2 +2656,zoppicare,3,2 +2657,mascella,3,2 +2658,soffermarsi,3,2 +2659,balzare,3,2 +2660,narici,3,2 +2661,gomitata,3,2 +2662,beccare,3,2 +2663,formicolio,3,2 +2664,contraccolpo,3,2 +2665,ribattere,3,2 +2666,stridio,3,2 +2667,alzare le spalle,3,2 +2668,manica,3,2 +2669,lieve,3,2 +2670,fiacco,3,2 +2671,bassifondi,3,2 +2672,austero,3,2 +2673,piombare,3,2 +2674,suscettibile,3,2 +2675,pulsare,3,2 +2676,lievito,3,2 +2677,nominare,3,2 +2678,vergognarsi,3,2 +2679,cenno,3,2 +2680,fascino,3,2 +2681,abisso,3,2 +2682,scricchiolare,3,2 +2683,rifiutare,3,2 +2684,disprezzare,3,2 +2685,misterioso,3,2 +2686,bagliore,3,2 +2687,luccichio,3,2 +2688,smorfia,3,2 +2689,coperchio,3,2 +2690,malizia,3,2 +2691,minaccia,3,2 +2692,trattenere,3,2 +2693,cedere,3,2 +2694,cipiglio,3,2 +2695,rabbrividire,3,2 +2696,strisciare,3,2 +2697,sogghigno,3,2 +2698,dolorante,3,2 +2699,teso,3,2 +2700,coscia,3,2 +2701,scompigliare,3,2 +2702,gocciolare,3,2 +2703,vita,3,2 +2704,slanciato,3,2 +2705,contorcersi,3,2 +2706,affiancato,3,2 +2707,vicolo,3,2 +2708,a pezzi,3,2 +2709,evitare,3,2 +2710,fronte,3,2 +2711,costringere,3,2 +2712,diga,3,2 +2713,umido,3,2 +2714,pisolino,3,2 +2715,gnocco,3,2 +2716,tenuta,3,2 +2717,prossimo,3,2 +2718,franco,3,2 +2719,concetto,3,2 +2720,pelle d'oca,3,2 +2721,era ora,3,2 +2722,colosso,3,2 +2723,sporgente,3,2 +2724,ridicolo,3,2 +2725,midollo,3,2 +2726,radunare,3,2 +2727,obbligare,3,2 +2728,riscattare,3,2 +2729,mischiare,3,2 +2730,sbandare,3,2 +2731,pullulare,3,2 +2732,di volta in volta,3,2 +2733,all'insaputa,3,2 +2734,ghiribizzo,3,2 +2735,oscillare,3,2 +2736,comunque,3,2 +2737,confessare,3,2 +2738,generare,3,2 +2739,legame,3,2 +2740,buttafuori,3,2 +2741,fendere,3,2 +2742,fannullone,3,2 +2743,esigere,3,2 +2744,lugubre,3,2 +2745,impasto,3,2 +2746,esaltare,3,2 +2747,enorme,3,2 +2748,squarcio,3,2 +2749,erede,3,2 +2750,intralcio,3,2 +2751,in quanto,3,2 +2752,infame,3,2 +2753,eccitare,3,2 +2754,indagare,3,2 +2755,ebreo,3,2 +2756,mite,3,2 +2757,tenebre,3,2 +2758,prole,3,2 +2759,ritrarre,3,2 +2760,blaterare,3,2 +2761,demolire,3,2 +2762,riferirsi,3,2 +2763,rottura,3,2 +2764,liscio,3,2 +2765,contaminare,3,2 +2766,legare,3,2 +2767,modello,3,2 +2768,rinforzare,3,2 +2769,limite,3,2 +2770,coerente,3,2 +2771,squallido,3,2 +2772,schivare,3,2 +2773,scarabocchio,3,2 +2774,applicare,3,2 +2775,impegnarsi,3,2 +2776,estensione,3,2 +2777,prevedere,3,2 +2778,due settimane,3,2 +2779,intravedere,3,2 +2780,mandria,3,2 +2781,gergo,3,2 +2782,complessivamente,3,2 +2783,accurato,3,2 +2784,stimare,3,2 +2785,riavvolgere,3,2 +2786,puro,3,2 +2787,sciocco,3,2 +2788,schizzinoso,3,2 +2789,stabile,3,2 +2790,sciopero,3,2 +2791,supporre,3,2 +2792,fremito,3,2 +2793,spingere,3,2 +2794,tregua,3,2 +2795,goffo,3,2 +2796,avvocato,3,2 +2797,avverso,3,2 +2798,cinghiale,3,2 +2799,lucidare,3,2 +2800,fascio,3,2 +2801,truffatore,3,2 +2802,cucciolo,3,2 +2803,rinnegare,3,2 +2804,sconforto,3,2 +2805,comprendere,3,2 +2806,prodezza,3,2 +2807,cruento,3,2 +2808,incendiare,3,2 +2809,balzo,3,2 +2810,figuriamoci,3,2 +2811,seccatura,3,2 +2812,strano,3,2 +2813,scavalcare,3,2 +2814,sbirciare,3,2 +2815,segnaposto,3,2 +2816,predicare,3,2 +2817,indipendentemente,3,2 +2818,linfa,3,2 +2819,intrufolarsi,3,2 +2820,scorrere,3,2 +2821,denunciare,3,2 +2822,contrastare,3,2 +2823,insegnamento,3,2 +2824,vuoto,3,2 +2825,deformare,3,2 +2826,ausiliario,3,2 +2827,disorientare,3,2 +2828,sbottare,3,2 +2829,in scatola,3,2 +2830,schiaffo,3,2 +2831,posate,3,2 +2832,terribile,3,2 +2833,bozza,3,2 +2834,piumone,3,2 +2835,attirare,3,2 +2836,forza d'animo,3,2 +2837,intoppo,3,2 +2838,affrettarsi,3,2 +2839,compromettere,3,2 +2840,rigoglioso,3,2 +2841,incantare,3,2 +2842,ingenuo,3,2 +2843,emarginato,3,2 +2844,pezza,3,2 +2845,smerciare,3,2 +2846,ficcare,3,2 +2847,gracile,3,2 +2848,scarafaggio,3,2 +2849,traballante,3,2 +2850,striminzito,3,2 +2851,furbo,3,2 +2852,lenire,3,2 +2853,ondata,3,2 +2854,vapore,3,2 +2855,solletico,3,2 +2856,melanzana,3,2 +2857,manzo,3,2 +2858,candeggina,3,2 +2859,ciotola,3,2 +2860,massiccio,3,2 +2861,zucchina,3,2 +2862,croccante,3,2 +2863,cetriolo,3,2 +2864,subdolo,3,2 +2865,fattoria,3,2 +2866,fiocco,3,2 +2867,wurstel,3,2 +2868,prosciutto,3,2 +2869,stizzito,3,2 +2870,bollitore,3,2 +2871,pagnotta,3,2 +2872,potente,3,2 +2873,tazza,3,2 +2874,pisello,3,2 +2875,arachide,3,2 +2876,peperone,3,2 +2877,flessibile,3,2 +2878,pentola,3,2 +2879,spericolato,3,2 +2880,sciacquare,3,2 +2881,panino,3,2 +2882,spugna,3,2 +2883,fornello,3,2 +2884,bicchiere,3,2 +2885,integrale,3,2 +2886,macchia,3,2 +2887,scivolo,3,2 +2888,nascondere,3,2 +2889,osare,3,2 +2890,offuscare,3,2 +2891,eliminazione,3,2 +2892,droga,3,2 +2893,sforzo,3,2 +2894,esilio,3,2 +2895,impiegare,3,2 +2896,frizzante,3,2 +2897,cautamente,3,2 +2898,truce,3,2 +2899,cappuccio,3,2 +2900,insorto,3,2 +2901,spintonare,3,2 +2902,souvenir,3,2 +2903,lavanderia,3,2 +2904,tiepido,3,2 +2905,aggiustare,3,2 +2906,sfogo,3,2 +2907,esterno,3,2 +2908,sbucciare,3,2 +2909,gonfio,3,2 +2910,scrostare,3,2 +2911,affilato,3,2 +2912,allentare,3,2 +2913,vassoio,3,2 +2914,ritirare,3,2 +2915,trattenuto,3,2 +2916,rapire,3,2 +2917,divertire,3,2 +2918,offrire,3,2 +2919,sfacciato,3,2 +2920,furia,3,2 +2921,alcolico,3,2 +2922,cavolo,3,2 +2923,cavolfiore,3,2 +2924,apprezzare,3,2 +2925,ceci,3,2 +2926,divulgare,3,2 +2927,debole,3,2 +2928,lentiggine,3,2 +2929,va al diavolo,3,2 +2930,pascolare,3,2 +2931,posteriore,3,2 +2932,appassionato,3,2 +2933,mestolo,3,2 +2934,sollevare,3,2 +2935,interruzione,3,2 +2936,lucchetto,3,2 +2937,importunare,3,2 +2938,curiosare,3,2 +2939,bruciatura,3,2 +2940,affronto,3,2 +2941,schiacciare,3,2 +2942,tessuto,3,2 +2943,pancia,3,2 +2944,magnate,3,2 +2945,bordo,3,2 +2946,smarrito,3,2 +2947,infuso,3,2 +2948,rovesciarsi,3,2 +2949,ideare,3,2 +2950,miscuglio,3,2 +2951,cucina,3,2 +2952,abbagliare,3,2 +2953,esaurire,3,2 +2954,scontento,3,2 +2955,incorporare,3,2 +2956,congedo,3,2 +2957,gregge,3,2 +2958,popolare,3,2 +2959,rinunciare,3,2 +2960,altezzoso,3,2 +2961,interazione,3,2 +2962,intromettersi,3,2 +2963,scherzo,3,2 +2964,deridere,3,2 +2965,principio,3,2 +2966,portamento,3,2 +2967,respiro,3,2 +2968,assaporare,3,2 +2969,trasandato,3,2 +2970,compiaciuto,3,2 +2971,convocare,3,2 +2972,parsimonia,3,2 +2973,refuso,3,2 +2974,sconvolgimento,3,2 +2975,conflitto,3,2 +2976,battere,3,2 +2977,infastidire,3,2 +2978,bombo,3,2 +2979,scolapasta,3,2 +2980,scombussolare,3,2 +2981,malattia,3,2 +2982,fugace,3,2 +2983,soffice,3,2 +2984,martello,3,2 +2985,nostalgia di casa,3,2 +2986,emanare,3,2 +2987,inaffidabile,3,2 +2988,patetico,3,2 +2989,ninna nanna,3,2 +2990,fresa,3,2 +2991,umore,3,2 +2992,angusto,3,2 +2993,ascesa,3,2 +2994,forbice,3,2 +2995,setoso,3,2 +2996,odore,3,2 +2997,rachide,3,2 +2998,stantio,3,2 +2999,puzza,3,2 +3000,sciame,3,2 +3001,arazzo,3,2 +3002,insopportabile,3,2 +3003,empio,3,2 +3004,ineffabile,3,2 +3005,guizzo,3,2 +3006,allettare,3,2 +3007,revisione,3,2 +3008,tramandare,3,2 +3009,conferire,3,2 +3010,sete di sangue,3,2 +3011,setola,3,2 +3012,presunzione,3,2 +3013,squilibrato,3,2 +3014,produrre,3,2 +3015,travolgere,3,2 +3016,azienda,3,2 +3017,mischia,3,2 +3018,vistoso,3,2 +3019,arroganza,3,2 +3020,scagliare,3,2 +3021,perdita,3,2 +3022,modesto,3,2 +3023,prezzemolo,3,2 +3024,saccheggiare,3,2 +3025,versare,3,2 +3026,filtrare,3,2 +3027,riva,3,2 +3028,ripudiare,3,2 +3029,assedio,3,2 +3030,sperperare,3,2 +3031,contesa,3,2 +3032,spettegolare,3,2 +3033,faticare,3,2 +3034,incontenibile,3,2 +3035,battuto,3,2 +3036,inorridito,3,2 +3037,amabile,3,2 +3038,sfotto',3,2 +3039,abbaiare,3,2 +3040,leccapiedi,3,2 +3041,accogliente,3,2 +3042,fingere,3,2 +3043,folletto,3,2 +3044,talento,3,2 +3045,negligenza,3,2 +3046,iperattivo,3,2 +3047,scarsezza,3,2 +3048,pancione,3,2 +3049,arrancare,3,2 +3050,borsetta,3,2 +3051,ruggine,3,2 +3052,ciondolare,3,2 +3053,stuzzicare,3,2 +3054,lattina,3,2 +3055,corteggiare,3,2 +3056,valutazione,3,2 +3057,furto,3,2 +3058,cronaca,3,2 +3059,deriva,3,2 +3060,tariffa,3,2 +3061,antagonista,3,2 +3062,contraddire,3,2 +3063,misurare,3,2 +3064,ingorgo,3,2 +3065,botola,3,2 +3066,dirottare,3,2 +3067,cardine,3,2 +3068,anzi,3,2 +3069,contrattempo,3,2 +3070,dramma,3,2 +3071,ponderare,3,2 +3072,rimedio,3,2 +3073,dilagante,3,2 +3074,ribollire,3,2 +3075,lacrima,3,2 +3076,assistenza,3,2 +3077,tra,3,2 +3078,smentire,3,2 +3079,sbiancare,3,2 +3080,masso,3,2 +3081,scolpire,3,2 +3082,favola,3,2 +3083,sguardo,3,2 +3084,gioia,3,2 +3085,adirato,3,2 +3086,confusione,3,2 +3087,superare,3,2 +3088,irascibile,3,2 +3089,stupro,3,2 +3090,salvia,3,2 +3091,sangue freddo,3,2 +3092,disprezzo,3,2 +3093,pedante,3,2 +3094,tensione,3,2 +3095,spettro,3,2 +3096,capitare,3,2 +3097,lamentarsi,3,2 +3098,falso,3,2 +3099,spostarsi,3,2 +3100,pettine,3,2 +3101,sussulto,3,2 +3102,affliggere,3,2 +3103,umiliarsi,3,2 +3104,trascinare,3,2 +3105,custode,3,2 +3106,infausto,3,2 +3107,molletta,3,2 +3108,medico,3,2 +3109,puntiglio,3,2 +3110,battuta,3,2 +3111,incursione,3,2 +3112,manifestazione,3,2 +3113,tendine,3,2 +3114,sgobbare,3,2 +3115,calpestare,3,2 +3116,assecondare,3,2 +3117,colonna portante,3,2 +3118,spaccone,3,2 +3119,rimuginare,3,2 +3120,cospirare,3,2 +3121,rettore,3,2 +3122,violino,3,2 +3123,fianco,3,2 +3124,punto morto,3,2 +3125,anticonformista,3,2 +3126,prospettiva,3,2 +3127,spillo,3,2 +3128,consapevole,3,2 +3129,teppista,3,2 +3130,pretendere,3,2 +3131,dimettersi,3,2 +3132,difetto,3,2 +3133,conforto,3,2 +3134,svenire,3,2 +3135,pioniere,3,2 +3136,provvedere,3,2 +3137,castigare,3,2 +3138,inzuppare,3,2 +3139,adeguatamente,3,2 +3140,stordito,3,2 +3141,planare,3,2 +3142,contrattare,3,2 +3143,allusione,3,2 +3144,incallito,3,2 +3145,cupo,3,2 +3146,calunnia,3,2 +3147,scivolare,3,2 +3148,snobbare,3,2 +3149,riporre,3,2 +3150,broncio,3,2 +3151,fasciare,3,2 +3152,acceleratore,3,2 +3153,saldare,3,2 +3154,mezzi,3,2 +3155,strizzare,3,2 +3156,tardivo,3,2 +3157,annerire,3,2 +3158,bancarella,3,2 +3159,mortificazione,3,2 +3160,aggrappare,3,2 +3161,brusco,3,2 +3162,screditare,3,2 +3163,depresso,3,2 +3164,impertinente,3,2 +3165,affezionato,3,2 +3166,raffica,3,2 +3167,guscio,3,2 +3168,marsupio,3,2 +3169,affidabile,3,2 +3170,strofinare,3,2 +3171,riservato,3,2 +3172,puntina,3,2 +3173,tonfo,3,2 +3174,adulare,3,2 +3175,disgraziato,3,2 +3176,splendente,3,2 +3177,albicocca,3,2 +3178,passante,3,2 +3179,sconvolto,3,2 +3180,assorto,3,2 +3181,impugnatura,3,2 +3182,immischiarsi,3,2 +3183,ricordo,3,2 +3184,nuvoloso,3,2 +3185,incentivo,3,2 +3186,pretesto,3,2 +3187,incline,3,2 +3188,impicciarsi,3,2 +3189,ritornello,3,2 +3190,disastro,3,2 +3191,accovacciarsi,3,2 +3192,afoso,3,2 +3193,brillo,3,2 +3194,spago,3,2 +3195,presumibilmente,3,2 +3196,pasticcio,3,2 +3197,spiraglio,3,2 +3198,fondersi,3,2 +3199,tubatura,3,2 +3200,evocare,3,2 +3201,temerario,3,2 +3202,deviazione,3,2 +3203,controversia,3,2 +3204,affascinare,3,2 +3205,perbacco,3,2 +3206,registro,3,2 +3207,mutuo,3,2 +3208,esigente,3,2 +3209,sondaggio,3,2 +3210,condimento,3,2 +3211,strattone,3,2 +3212,imperterrito,3,2 +3213,valoroso,3,2 +3214,guaito,3,2 +3215,maggiordomo,3,2 +3216,cinico,3,2 +3217,culla,3,2 +3218,monouso,3,2 +3219,inimicizia,3,2 +3220,equo,3,2 +3221,nocciola,3,2 +3222,frenetico,3,2 +3223,improvvisato,3,2 +3224,tempra,3,2 +3225,colare,3,2 +3226,revisionare,3,2 +3227,opuscolo,3,2 +3228,tendenza,3,2 +3229,ansimare,3,2 +3230,fornitore,3,2 +3231,ambito,3,2 +3232,fisima,3,2 +3233,prodigio,3,2 +3234,emendare,3,2 +3235,attribuire,3,2 +3236,barista,3,2 +3237,allevare,3,2 +3238,coltura,3,2 +3239,figurarsi,3,2 +3240,portami,3,2 +3241,imperfezione,3,2 +3242,pulce,3,2 +3243,consacrare,3,2 +3244,cavo,3,2 +3245,monotono,3,2 +3246,caos,3,2 +3247,apprensione,3,2 +3248,pegno,3,2 +3249,localizzare,3,2 +3250,colmo,3,2 +3251,resistere,3,2 +3252,tuorlo,3,2 +3253,tormentare,3,2 +3254,incontro,3,2 +3255,autorizzazione,3,2 +3256,sfatare,3,2 +3257,facciata,3,2 +3258,afferrare,3,2 +3259,frangia,3,2 +3260,polverone,3,2 +3261,lepre,3,2 +3262,efferato,3,2 +3263,infondere,3,2 +3264,interferire,3,2 +3265,torbido,3,2 +3266,malgrado,3,2 +3267,in ritardo,3,2 +3268,opinionista,3,2 +3269,risarcimento,3,2 +3270,strappare,3,2 +3271,flessuoso,3,2 +3272,sciocchezza,3,2 +3273,catturare,3,2 +3274,oltre,3,2 +3275,risultato finale,3,2 +3276,coprirsi bene,3,2 +3277,per caso,3,2 +3278,recuperare,3,2 +3279,capo,3,2 +3280,fare un salto,3,2 +3281,fai piano,3,2 +3282,credulone,3,2 +3283,orientato,3,2 +3284,ricorda le mie parole,3,2 +3285,birichino,3,2 +3286,in forma,3,2 +3287,botta e risposta,3,2 +3288,escludere,3,2 +3289,avviarci,3,2 +3290,tampone,3,2 +3291,allontanare,3,2 +3292,informatore,3,2 +3293,alleviare,3,2 +3294,coagulare,3,2 +3295,arruolare,3,2 +3296,emblema,3,2 +3297,per quattro soldi,3,2 +3298,seguire la corrente,3,2 +3299,impastare,3,2 +3300,arrangiarsi,3,2 +3301,testa a testa,3,2 +3302,parente prossimo,3,2 +3303,ficcanaso,3,2 +3304,precipitare,3,2 +3305,permaloso,3,2 +3306,rassicurare,3,2 +3307,tenace,3,2 +3308,alga,3,2 +3309,cosi' per dire,3,2 +3310,sotto tiro,3,2 +3311,aceto,3,2 +3312,chiudi il becco,3,2 +3313,dacci un taglio,3,2 +3314,impassibile,3,2 +3315,a scoppio ritardato,3,2 +3316,sbalordire,3,2 +3317,sciacquone,3,2 +3318,riagganciare,3,2 +3319,ricucire i rapporti,3,2 +3320,all'ultimo secondo,3,2 +3321,passo e chiudo,3,2 +3322,salotto,3,2 +3323,fare la predica,3,2 +3324,prugna,3,2 +3325,fetore,3,2 +3326,cambiamento radicale,3,2 +3327,felpa,3,2 +3328,banco di prova,3,2 +3329,essere all'altezza,3,2 +3330,controverso,3,2 +3331,andare a finire,3,2 +3332,chiave inglese,3,2 +3333,infangare,3,2 +3334,ventilatore,3,2 +3335,inetto,3,2 +3336,abbandonato,3,2 +3337,cartilagine,3,2 +3338,tallone,3,2 +3339,superstite,3,2 +3340,mettere in dubbio,3,2 +3341,schernire,3,2 +3342,insignificante,3,2 +3343,gioco di parole,3,2 +3344,marmaglia,3,2 +3345,fascia,3,2 +3346,stridulo,3,2 +3347,femminuccia,3,2 +3348,pendere,3,2 +3349,aspro,3,2 +3350,sbrogliare,3,2 +3351,vagabondo,3,2 +3352,lana,3,2 +3353,a galla,3,2 +3354,barbabietola,3,2 +3355,impacciato,3,2 +3356,garanzia,3,2 +3357,dilemma,3,2 +3358,convalidare,3,2 +3359,stampella,3,2 +3360,randello,3,2 +3361,disingannare,3,2 +3362,filo interdentale,3,2 +3363,diceria,3,2 +3364,sferruzzare,3,2 +3365,imparare a memoria,3,2 +3366,consultare,3,2 +3367,tradizioni,3,2 +3368,imitazione,3,2 +3369,spiegazzare,3,2 +3370,buona sorte,3,2 +3371,gareggiare,3,2 +3372,scagionare,3,2 +3373,miscela,3,2 +3374,brodo,3,2 +3375,sgretolarsi,3,2 +3376,commissione,3,2 +3377,espediente,3,2 +3378,racimolare,3,2 +3379,frivolo,3,2 +3380,inviolabile,3,2 +3381,granturco,3,2 +3382,misero,3,2 +3383,boccone,3,2 +3384,panacea,3,2 +3385,immacolato,3,2 +3386,poltiglia,3,2 +3387,superficiale,3,2 +3388,tosare,3,2 +3389,mandato,3,2 +3390,lacero,3,2 +3391,ramoscello,3,2 +3392,velluto,3,2 +3393,bachelor,3,1 +3394,scapolo,3,2 +3395,bemused,3,1 +3396,perplesso,3,2 +3397,bilk,3,1 +3398,frodare,3,2 +3399,bite the bullet,3,1 +3400,stringere i denti,3,2 +3401,breach,3,1 +3402,violazione,3,2 +3403,bruise,3,1 +3404,livido,3,2 +3405,chicken out,3,1 +3406,tirarsi indietro,3,2 +3407,cork,3,1 +3408,sughero,3,2 +3409,crane,3,1 +3410,gru,3,2 +3411,dub,3,1 +3412,doppiare,3,2 +3413,embroider,3,1 +3414,ricamare,3,2 +3415,glower,3,1 +3416,guardare torvo,3,2 +3417,hatchet,3,1 +3418,accetta,3,2 +3419,kick back,3,1 +3420,rilassarsi,3,2 +3421,overpower,3,1 +3422,sopraffare,3,2 +3423,patrol,3,1 +3424,pattuglia,3,2 +3425,pittance,3,1 +3426,inezia,3,2 +3427,sewer,3,1 +3428,fogna,3,2 +3429,sorcerer,3,1 +3430,stregone,3,2 +3431,uncork,3,1 +3432,stappare,3,2 +3433,coy,3,1 +3434,schivo,3,2 +3435,dew,3,1 +3436,rugiada,3,2 +3437,entreaty,3,1 +3438,supplica,3,2 +3439,fierce,3,1 +3440,impetuoso,3,2 +3441,groin,3,1 +3442,inguine,3,2 +3443,maim,3,1 +3444,mutilare,3,2 +3445,mores,3,1 +3446,usanze,3,2 +3447,nanny,3,1 +3448,tata,3,2 +3449,ox,3,1 +3450,bue,3,2 +3451,pensive,3,1 +3452,pensieroso,3,2 +3453,peril,3,1 +3454,serio pericolo,3,2 +3455,ransom,3,1 +3456,riscatto,3,2 +3457,smith,3,1 +3458,fabbro,3,2 +3459,sprain,3,1 +3460,distorsione,3,2 +3461,stutter,3,1 +3462,balbettare,3,2 +3463,suffice,3,1 +3464,bastare,3,2 +3465,swamp,3,1 +3466,sommergere,3,2 +3467,tally,3,1 +3468,conteggio,3,2 +3469,tawdry,3,1 +3470,pacchiano,3,2 +3471,thimble,3,1 +3472,ditale,3,2 +3473,beside,3,1 +3474,accanto,3,2 +3475,beside the point,3,1 +3476,non c'entra,3,2 +3477,bladder,3,1 +3478,vescica,3,2 +3479,boast,3,1 +3480,vantare,3,2 +3481,daze,3,1 +3482,stordimento,3,2 +3483,fussy,3,1 +3484,pignolo,3,2 +3485,garrulous,3,1 +3486,chiacchierone,3,2 +3487,lecherous,3,1 +3488,lascivo,3,2 +3489,muzzle,3,1 +3490,museruola,3,2 +3491,noxious,3,1 +3492,nocivo,3,2 +3493,offset,3,1 +3494,compensare,3,2 +3495,ordeal,3,1 +3496,calvario,3,2 +3497,pantyhose,3,1 +3498,collant,3,2 +3499,repentant,3,1 +3500,pentito,3,2 +3501,ripple,3,1 +3502,increspatura,3,2 +3503,scabbard,3,1 +3504,fodero,3,2 +3505,sheath,3,1 +3506,guaina,3,2 +3507,sprawl,3,1 +3508,distendersi,3,2 +3509,talkative,3,1 +3510,loquace,3,2 +3511,uncouth,3,1 +3512,rozzo,3,2 +3513,breathtaking,3,1 +3514,mozzafiato,3,2 +3515,cougar,3,1 +3516,puma,3,2 +3517,crawl,3,1 +3518,gattonare,3,2 +3519,elsewhere,3,1 +3520,altrove,3,2 +3521,enclose,3,1 +3522,racchiudere,3,2 +3523,handcuff,3,1 +3524,ammanettare,3,2 +3525,hold on,3,1 +3526,tenere duro,3,2 +3527,midwife,3,1 +3528,ostetrica,3,2 +3529,outshine,3,1 +3530,mettere in ombra,3,2 +3531,resemble,3,1 +3532,assomigliare,3,2 +3533,right on cue,3,1 +3534,al momento giusto,3,2 +3535,roam,3,1 +3536,vagare,3,2 +3537,scrawny,3,1 +3538,pelle e ossa,3,2 +3539,shortage,3,1 +3540,carenza,3,2 +3541,sleep in,3,1 +3542,dormire fino a tardi,3,2 +3543,splurge,3,1 +3544,scialacquare,3,2 +3545,tablecloth,3,1 +3546,tovaglia,3,2 +3547,trespass,3,1 +3548,sconfinare,3,2 +3549,turn a blind eye,3,1 +3550,chiudere un occhio,3,2 +3551,upbeat,3,1 +3552,ottimista,3,2 +3553,aftermath,3,1 +3554,conseguenze,3,2 +3555,broadcast,3,1 +3556,trasmissione,3,2 +3557,circuitous,3,1 +3558,tortuoso,3,2 +3559,contempt,3,1 +3560,dispregio,3,2 +3561,curtain,3,1 +3562,tenda,3,2 +3563,deft,3,1 +3564,abile,3,2 +3565,defy,3,1 +3566,sfidare,3,2 +3567,dial,3,1 +3568,comporre,3,2 +3569,district,3,1 +3570,quartiere,3,2 +3571,dummy,3,1 +3572,manichino,3,2 +3573,firefly,3,1 +3574,lucciola,3,2 +3575,flaunt,3,1 +3576,sfoggiare,3,2 +3577,fur,3,1 +3578,pelliccia,3,2 +3579,gulp,3,1 +3580,sorso,3,2 +3581,hiss,3,1 +3582,sibilo,3,2 +3583,pack,3,1 +3584,branco,3,2 +3585,pierce,3,1 +3586,forare,3,2 +3587,praise,3,1 +3588,elogio,3,2 +3589,sidestep,3,1 +3590,eludere,3,2 +3591,watchful,3,1 +3592,vigile,3,2 +3593,advocate,3,1 +3594,sostenitore,3,2 +3595,ailment,3,1 +3596,indisposizione,3,2 +3597,coffer,3,1 +3598,forziere,3,2 +3599,dent,3,1 +3600,ammaccatura,3,2 +3601,dither,3,1 +3602,indecisione,3,2 +3603,feisty,3,1 +3604,esuberante,3,2 +3605,fief,3,1 +3606,feudo,3,2 +3607,funnel,3,1 +3608,imbuto,3,2 +3609,grope,3,1 +3610,brancolare,3,2 +3611,heyday,3,1 +3612,apogeo,3,2 +3613,maid,3,1 +3614,domestica,3,2 +3615,motley,3,1 +3616,eterogeneo,3,2 +3617,nick,3,1 +3618,intaccare,3,2 +3619,outermost,3,1 +3620,piu' esterno,3,2 +3621,plea,3,1 +3622,appello,3,2 +3623,rue,3,1 +3624,rimpiangere,3,2 +3625,seam,3,1 +3626,cucitura,3,2 +3627,standoff,3,1 +3628,stallo,3,2 +3629,tiptoe,3,1 +3630,in punta di piedi,3,2 +3631,vibe,3,1 +3632,atmosfera,3,2 +3633,abate,3,1 +3634,ridurre,3,2 +3635,abridge,3,1 +3636,abbreviare,3,2 +3637,abrupt,3,1 +3638,improvviso,3,2 +3639,bane,3,1 +3640,rovina,3,2 +3641,beguile,3,1 +3642,ammaliare,3,2 +3643,blunder,3,1 +3644,abbaglio,3,2 +3645,blurb,3,1 +3646,trafiletto,3,2 +3647,drool,3,1 +3648,sbavare,3,2 +3649,enliven,3,1 +3650,ravvivare,3,2 +3651,furniture,3,1 +3652,arredamento,3,2 +3653,glut,3,1 +3654,eccesso,3,2 +3655,jocular,3,1 +3656,scherzoso,3,2 +3657,mislay,3,1 +3658,smarrire,3,2 +3659,overstate,3,1 +3660,enfatizzare,3,2 +3661,pertain,3,1 +3662,riguardare,3,2 +3663,quagmire,3,1 +3664,pantano,3,2 +3665,rephrase,3,1 +3666,riformulare,3,2 +3667,soar,3,1 +3668,librarsi,3,2 +3669,unpalatable,3,1 +3670,sgradevole,3,2 +3671,utterance,3,1 +3672,affermazione,3,2 +3673,backlash,3,1 +3674,ripercussione,3,2 +3675,badmouth,3,1 +3676,sparlare,3,2 +3677,bereft of,3,1 +3678,privo di,3,2 +3679,blemish,3,1 +3680,pecca,3,2 +3681,deter,3,1 +3682,dissuadere,3,2 +3683,dishearten,3,1 +3684,avvilire,3,2 +3685,encroach,3,1 +3686,invadere,3,2 +3687,ensnare,3,1 +3688,intrappolare,3,2 +3689,fallacy,3,1 +3690,incongruenza,3,2 +3691,infringe,3,1 +3692,violare,3,2 +3693,likelihood,3,1 +3694,possibilita',3,2 +3695,offhand,3,1 +3696,su due piedi,3,2 +3697,onward,3,1 +3698,in avanti,3,2 +3699,prior,3,1 +3700,precedente,3,2 +3701,query,3,1 +3702,interrogare,3,2 +3703,ravenous,3,1 +3704,famelico,3,2 +3705,revere,3,1 +3706,riverire,3,2 +3707,sham,3,1 +3708,finzione,3,2 +3709,travesty,3,1 +3710,parodia,3,2 +3711,typify,3,1 +3712,caratterizzare,3,2 +3713,avail,3,1 +3714,avvalersi,3,2 +3715,chairman,3,1 +3716,presidente,3,2 +3717,clique,3,1 +3718,cricca,3,2 +3719,dab,3,1 +3720,tamponare,3,2 +3721,despicable,3,1 +3722,spregevole,3,2 +3723,enfeeble,3,1 +3724,indebolire,3,2 +3725,essay,3,1 +3726,tema,3,2 +3727,expendable,3,1 +3728,sacrificabile,3,2 +3729,fluster,3,1 +3730,agitazione,3,2 +3731,foolhardy,3,1 +3732,avventato,3,2 +3733,forbearance,3,1 +3734,tolleranza,3,2 +3735,forefront,3,1 +3736,prima linea,3,2 +3737,geek,3,1 +3738,secchione,3,2 +3739,hoarse,3,1 +3740,rauco,3,2 +3741,loath,3,1 +3742,restio,3,2 +3743,lockdown,3,1 +3744,isolamento,3,2 +3745,scarf,3,1 +3746,sciarpa,3,2 +3747,staple,3,1 +3748,di base,3,2 +3749,terse,3,1 +3750,conciso,3,2 +3751,womb,3,1 +3752,grembo,3,2 +3753,ajar,3,1 +3754,socchiuso,3,2 +3755,bootleg,3,1 +3756,di contrabbando,3,2 +3757,cocoon,3,1 +3758,bozzolo,3,2 +3759,commodity,3,1 +3760,merce,3,2 +3761,compass,3,1 +3762,bussola,3,2 +3763,connoisseur,3,1 +3764,intenditore,3,2 +3765,dandruff,3,1 +3766,forfora,3,2 +3767,devious,3,1 +3768,infido,3,2 +3769,garble,3,1 +3770,alterare,3,2 +3771,gate,3,1 +3772,cancello,3,2 +3773,glutton,3,1 +3774,ingordo,3,2 +3775,infer,3,1 +3776,dedurre,3,2 +3777,poppy,3,1 +3778,papavero,3,2 +3779,probe,3,1 +3780,sonda,3,2 +3781,renege,3,1 +3782,venire meno,3,2 +3783,shin,3,1 +3784,stinco,3,2 +3785,sickle,3,1 +3786,falcetto,3,2 +3787,skunk,3,1 +3788,puzzola,3,2 +3789,treatise,3,1 +3790,trattato,3,2 +3791,wield,3,1 +3792,detenere,3,2 +3793,ashtray,3,1 +3794,posacenere,3,2 +3795,binder,3,1 +3796,raccoglitore,3,2 +3797,bounty,3,1 +3798,taglia,3,2 +3799,clatter,3,1 +3800,sferragliare,3,2 +3801,cleavage,3,1 +3802,scollatura,3,2 +3803,convene,3,1 +3804,riunire,3,2 +3805,crevice,3,1 +3806,crepa,3,2 +3807,flagon,3,1 +3808,caraffa,3,2 +3809,flout,3,1 +3810,farsi beffa,3,2 +3811,frostbite,3,1 +3812,congelamento,3,2 +3813,hoist,3,1 +3814,issare,3,2 +3815,naughty,3,1 +3816,disobbediente,3,2 +3817,nifty,3,1 +3818,ingegnoso,3,2 +3819,oath,3,1 +3820,promessa,3,2 +3821,personnel,3,1 +3822,personale,3,2 +3823,rejuvenate,3,1 +3824,ringiovanire,3,2 +3825,scramble,3,1 +3826,arrampicarsi,3,2 +3827,snide,3,1 +3828,malizioso,3,2 +3829,snore,3,1 +3830,russare,3,2 +3831,worthwhile,3,1 +3832,vale la pena,3,2 +3833,apt,3,1 +3834,adatto,3,2 +3835,brunt,3,1 +3836,impatto,3,2 +3837,burrow,3,1 +3838,tana,3,2 +3839,chubby,3,1 +3840,paffuto,3,2 +3841,debar,3,1 +3842,bandire,3,2 +3843,dump,3,1 +3844,scaricare,3,2 +3845,fed up,3,1 +3846,stufo,3,2 +3847,gnat,3,1 +3848,moscerino,3,2 +3849,grime,3,1 +3850,sporcizia,3,2 +3851,impair,3,1 +3852,pregiudicare,3,2 +3853,kinship,3,1 +3854,parentela,3,2 +3855,landfill,3,1 +3856,discarica,3,2 +3857,lead,3,1 +3858,piombo,3,2 +3859,mole,3,1 +3860,talpa,3,2 +3861,nag,3,1 +3862,assillare,3,2 +3863,outwit,3,1 +3864,raggirare,3,2 +3865,pudding,3,1 +3866,budino,3,2 +3867,underuse,3,1 +3868,sottoutilizzare,3,2 +3869,uppity,3,1 +3870,arrogante,3,2 +3871,veritable,3,1 +3872,vero e proprio,3,2 +3873,although,3,1 +3874,anche se,3,2 +3875,billboard,3,1 +3876,manifesto,3,2 +3877,brook,3,1 +3878,ruscello,3,2 +3879,brush,3,1 +3880,pennello,3,2 +3881,concern,3,1 +3882,preoccupazione,3,2 +3883,debriefing,3,1 +3884,rapporto,3,2 +3885,dexterity,3,1 +3886,destrezza,3,2 +3887,duct,3,1 +3888,condotto,3,2 +3889,due,3,1 +3890,scadere,3,2 +3891,furthermore,3,1 +3892,inoltre,3,2 +3893,halve,3,1 +3894,dimezzare,3,2 +3895,handpick,3,1 +3896,selezionare,3,2 +3897,henceforth,3,1 +3898,d'ora in poi,3,2 +3899,leeway,3,1 +3900,margine d'azione,3,2 +3901,meanwhile,3,1 +3902,nel frattempo,3,2 +3903,otherwise,3,1 +3904,altrimenti,3,2 +3905,regard,3,1 +3906,considerare,3,2 +3907,scepter,3,1 +3908,scettro,3,2 +3909,severance,3,1 +3910,separazione,3,2 +3911,yearn,3,1 +3912,anelare,3,2 +3913,amiss,3,1 +3914,fuori luogo,3,2 +3915,assess,3,1 +3916,valutare,3,2 +3917,attain,3,1 +3918,raggiungere,3,2 +3919,chore,3,1 +3920,lavoretto,3,2 +3921,compelled,3,1 +3922,costretto,3,2 +3923,detrimental,3,1 +3924,dannoso,3,2 +3925,discern,3,1 +3926,discernere,3,2 +3927,dull,3,1 +3928,noioso,3,2 +3929,grasp,3,1 +3930,capire,3,2 +3931,intent,3,1 +3932,intenzione,3,2 +3933,nourish,3,1 +3934,coltivare,3,2 +3935,obtain,3,1 +3936,ottenere,3,2 +3937,profound,3,1 +3938,intenso,3,2 +3939,prompt,3,1 +3940,immediato,3,2 +3941,pudgy,3,1 +3942,grassottello,3,2 +3943,seldom,3,1 +3944,raramente,3,2 +3945,timeliness,3,1 +3946,tempestivita',3,2 +3947,topnotch,3,1 +3948,eccellente,3,2 +3949,unwind,3,1 +3950,staccare,3,2 +3951,verbose,3,1 +3952,prolisso,3,2 +3953,banister,3,1 +3954,ringhiera,3,2 +3955,burp,3,1 +3956,rutto,3,2 +3957,cuddle,3,1 +3958,coccolare,3,2 +3959,endeavor,3,1 +3960,impegno,3,2 +3961,ensue,3,1 +3962,conseguire,3,2 +3963,fondle,3,1 +3964,accarezzare,3,2 +3965,herald,3,1 +3966,annunciare,3,2 +3967,intertwine,3,1 +3968,intrecciare,3,2 +3969,liaison,3,1 +3970,intermediario,3,2 +3971,loiter,3,1 +3972,gironzolare,3,2 +3973,mainstream,3,1 +3974,convenzionale,3,2 +3975,puddle,3,1 +3976,pozzanghera,3,2 +3977,retailer,3,1 +3978,rivenditore,3,2 +3979,stain,3,1 +3980,macchiare,3,2 +3981,stalwart,3,1 +3982,prode,3,2 +3983,stir up,3,1 +3984,fomentare,3,2 +3985,stuffy,3,1 +3986,soffocante,3,2 +3987,throng,3,1 +3988,folla,3,2 +3989,upcoming,3,1 +3990,in arrivo,3,2 +3991,wipe,3,1 +3992,pulire,3,2 +3993,adrift,3,1 +3994,alla deriva,3,2 +3995,appliance,3,1 +3996,elettrodomestico,3,2 +3997,arouse,3,1 +3998,stimolare,3,2 +3999,barren,3,1 +4000,sterile,3,2 +4001,cluster,3,1 +4002,raggruppamento,3,2 +4003,dearth,3,1 +4004,scarsita',3,2 +4007,esplanade,3,1 +4008,lungomare,3,2 +4009,expenditure,3,1 +4010,spesa,3,2 +4011,lilt,3,1 +4012,cadenza,3,2 +4013,lump,3,1 +4014,grumo,3,2 +4015,muse,3,1 +4016,riflettere,3,2 +4017,namesake,3,1 +4018,omonimo,3,2 +4019,outlandish,3,1 +4020,stravagante,3,2 +4021,pander,3,1 +4022,assecondare,3,2 +4023,persevere,3,1 +4024,persistere,3,2 +4025,popsicle,3,1 +4026,ghiacciolo,3,2 +4027,reproach,3,1 +4028,rimprovero,3,2 +4029,swerve,3,1 +4030,sterzare,3,2 +4031,victimize,3,1 +4032,perseguitare,3,2 +4033,aplomb,3,1 +4034,disinvoltura,3,2 +4035,backslide,3,1 +4036,ricadere,3,2 +4037,barrel,3,1 +4038,barile,3,2 +4039,brat,3,1 +4040,moccioso,3,2 +4041,coldness,3,1 +4042,freddezza,3,2 +4043,covet,3,1 +4044,invidiare,3,2 +4045,crevasse,3,1 +4046,crepaccio,3,2 +4047,environs,3,1 +4048,dintorni,3,2 +4049,frigid,3,1 +4050,gelido,3,2 +4051,girth,3,1 +4052,circonferenza,3,2 +4053,grisly,3,1 +4054,macabro,3,2 +4055,lanky,3,1 +4056,dinoccolato,3,2 +4057,lass,3,1 +4058,fanciulla,3,2 +4059,majestic,3,1 +4060,maestoso,3,2 +4061,pejorative,3,1 +4062,dispregiativo,3,2 +4063,peruse,3,1 +4064,esaminare,3,2 +4065,refuge,3,1 +4066,riparo,3,2 +4067,reiterate,3,1 +4068,reiterare,3,2 +4069,skirmish,3,1 +4070,scaramuccia,3,2 +4071,slate,3,1 +4072,ardesia,3,2 +4073,askance,3,1 +4074,con sospetto,3,2 +4075,blindside,3,1 +4076,alla sprovvista,3,2 +4077,comply,3,1 +4078,attenersi,3,2 +4079,erasure,3,1 +4080,cancellatura,3,2 +4081,landslide,3,1 +4082,frana,3,2 +4083,lease,3,1 +4084,locazione,3,2 +4085,opt,3,1 +4086,optare,3,2 +4087,piggyback,3,1 +4088,a cavalluccio,3,2 +4089,rebuff,3,1 +4090,ripulsa,3,2 +4091,replenish,3,1 +4092,rifornire,3,2 +4093,scoundrel,3,1 +4094,farabutto,3,2 +4095,shibboleth,3,1 +4096,parola d'ordine,3,2 +4097,sightsee,3,1 +4098,giro turistico,3,2 +4099,spurn,3,1 +4100,respingere,3,2 +4101,tantamount,3,1 +4102,equivalente,3,2 +4103,telltale,3,1 +4104,spione,3,2 +4105,unspoilt,3,1 +4106,incontaminato,3,2 +4107,whatnot,3,1 +4108,via dicendo,3,2 +4109,whittle,3,1 +4110,intagliare,3,2 +4111,wreak,3,1 +4112,provocare,3,2 +4113,adamant,3,1 +4114,irremovibile,3,2 +4115,appetiser,3,1 +4116,aperitivo,3,2 +4117,bloodshot,3,1 +4118,occhi rossi,3,2 +4119,defile,3,1 +4120,profanare,3,2 +4121,demotion,3,1 +4122,retrocessione,3,2 +4123,embroil,3,1 +4124,coinvolgere,3,2 +4125,enact,3,1 +4126,promulgare,3,2 +4127,felony,3,1 +4128,crimine,3,2 +4129,fib,3,1 +4130,frottola,3,2 +4131,foresight,3,1 +4132,lungimiranza,3,2 +4133,gurney,3,1 +4134,barella,3,2 +4135,indict,3,1 +4136,incriminare,3,2 +4137,letdown,3,1 +4138,deludere,3,2 +4139,mar,3,1 +4140,guastare,3,2 +4141,outskirts,3,1 +4142,sobborghi,3,2 +4143,outspoken,3,1 +4144,esplicito,3,2 +4145,plunge,3,1 +4146,tuffo,3,2 +4147,rebut,3,1 +4148,confutare,3,2 +4149,smuggle,3,1 +4150,contrabbandare,3,2 +4151,vacate,3,1 +4152,sgombrare,3,2 +4153,aground,3,1 +4154,arenare,3,2 +4155,brazen,3,1 +4156,impudente,3,2 +4157,exempt,3,1 +4158,esonerare,3,2 +4159,fallow,3,1 +4160,incolto,3,2 +4161,foreclose,3,1 +4162,pignorare,3,2 +4163,lobby,3,1 +4164,atrio,3,2 +4165,lopsided,3,1 +4166,asimmetrico,3,2 +4167,outright,3,1 +4168,immediatamente,3,2 +4169,paltry,3,1 +4170,irrisorio,3,2 +4171,rant,3,1 +4172,inveire,3,2 +4173,rave,3,1 +4174,delirare,3,2 +4175,repository,3,1 +4176,deposito,3,2 +4177,seepage,3,1 +4178,infiltrazione,3,2 +4179,shred,3,1 +4180,brandello,3,2 +4181,sludge,3,1 +4182,melma,3,2 +4183,smear,3,1 +4184,spalmare,3,2 +4185,sniff,3,1 +4186,annusare,3,2 +4187,subsist,3,1 +4188,sussistere,3,2 +4189,waft,3,1 +4190,soffio,3,2 +4191,wag,3,1 +4192,scodinzolare,3,2 +4193,claptrap,3,1 +4194,sproloquio,3,2 +4195,curfew,3,1 +4196,coprifuoco,3,2 +4197,dainty,3,1 +4198,delicato,3,2 +4199,flunk,3,1 +4200,cannare,3,2 +4201,fuse,3,1 +4202,miccia,3,2 +4203,gloat,3,1 +4204,gongolare,3,2 +4205,grouchy,3,1 +4206,brontolone,3,2 +4207,lather,3,1 +4208,insaponare,3,2 +4209,munch,3,1 +4210,ruminare,3,2 +4211,noose,3,1 +4212,cappio,3,2 +4213,rasp,3,1 +4214,raspare,3,2 +4215,reel,3,1 +4216,vacillare,3,2 +4217,rehash,3,1 +4218,rimaneggiare,3,2 +4219,reprieve,3,1 +4220,grazia,3,2 +4221,ruinous,3,1 +4222,rovinoso,3,2 +4223,snorkel,3,1 +4224,boccaglio,3,2 +4225,somersault,3,1 +4226,capriola,3,2 +4227,tender,3,1 +4228,tenero,3,2 +4229,wallow,3,1 +4230,sguazzare,3,2 +4231,wont,3,1 +4232,consuetudine,3,2 +4233,artichoke,3,1 +4234,carciofo,3,2 +4235,blithe,3,1 +4236,sconsiderato,3,2 +4237,boisterous,3,1 +4238,chiassoso,3,2 +4239,bra,3,1 +4240,reggiseno,3,2 +4241,chafe,3,1 +4242,irritazione,3,2 +4243,choppy,3,1 +4244,increspato,3,2 +4245,condone,3,1 +4246,tollerare,3,2 +4247,crisp,3,1 +4248,nitido,3,2 +4249,dislodge,3,1 +4250,smuovere,3,2 +4251,easy going,3,1 +4252,affabile,3,2 +4253,egghead,3,1 +4254,intellettuale,3,2 +4255,glimmer,3,1 +4256,barlume,3,2 +4257,hurdle,3,1 +4258,ostacolo,3,2 +4259,overt,3,1 +4260,evidente,3,2 +4261,pucker,3,1 +4262,corrugare,3,2 +4263,rattle,3,1 +4264,tintinnare,3,2 +4265,shriek,3,1 +4266,strillare,3,2 +4267,starch,3,1 +4268,amido,3,2 +4269,tingle,3,1 +4270,fremere,3,2 +4271,unswerving,3,1 +4272,incrollabile,3,2 +4273,antsy,3,1 +4274,irrequieto,3,2 +4275,balmy,3,1 +4276,temperato,3,2 +4277,behemoth,3,1 +4278,gigante,3,2 +4279,cheetah,3,1 +4280,ghepardo,3,2 +4281,complacent,3,1 +4282,soddisfatto,3,2 +4283,corral,3,1 +4284,recinto,3,2 +4285,curtsy,3,1 +4286,riverenza,3,2 +4287,desultory,3,1 +4288,saltuario,3,2 +4289,elated,3,1 +4290,euforico,3,2 +4291,fennel,3,1 +4292,finocchio,3,2 +4293,haywire,3,1 +4294,fuori controllo,3,2 +4295,jaded,3,1 +4296,sfinito,3,2 +4297,lax,3,1 +4298,permissivo,3,2 +4299,lettuce,3,1 +4300,lattuga,3,2 +4301,mulch,3,1 +4302,concime,3,2 +4303,quake,3,1 +4304,sisma,3,2 +4305,revile,3,1 +4306,ingiuriare,3,2 +4307,salve,3,1 +4308,pomata,3,2 +4309,trinket,3,1 +4310,gingillo,3,2 +4311,unflappable,3,1 +4312,flemmatico,3,2 +4313,beseech,3,1 +4314,supplicare,3,2 +4315,breeze,3,1 +4316,brezza,3,2 +4319,caw,3,1 +4320,gracchiare,3,2 +4321,crimson,3,1 +4322,cremisi,3,2 +4323,dusky,3,1 +4324,scuro,3,2 +4325,fir,3,1 +4326,abete,3,2 +4327,garb,3,1 +4328,abbigliamento,3,2 +4329,glance,3,1 +4330,occhiata,3,2 +4333,hue,3,1 +4334,tonalita',3,2 +4335,lapel,3,1 +4336,risvolto,3,2 +4337,log,3,1 +4338,ceppo,3,2 +4339,mistress,3,1 +4340,padrona,3,2 +4341,oat,3,1 +4342,avena,3,2 +4343,pledge,3,1 +4344,promettere,3,2 +4345,pod,3,1 +4346,baccello,3,2 +4347,revamp,3,1 +4348,rinnovamento,3,2 +4349,sedulous,3,1 +4350,assiduo,3,2 +4351,visage,3,1 +4352,viso,3,2 +4353,airborne,3,1 +4354,per via aerea,3,2 +4355,appal,3,1 +4356,sconvolgere,3,2 +4357,begrudge,3,1 +4358,a malincuore,3,2 +4359,cast,3,1 +4360,gettare,3,2 +4361,crosswise,3,1 +4362,trasversale,3,2 +4363,entrails,3,1 +4364,viscere,3,2 +4365,fern,3,1 +4366,felce,3,2 +4367,handout,3,1 +4368,elemosina,3,2 +4369,lousy,3,1 +4370,pessimo,3,2 +4371,poplar,3,1 +4372,pioppo,3,2 +4373,ransack,3,1 +4374,frugare,3,2 +4375,sear,3,1 +4376,rosolare,3,2 +4377,swat,3,1 +4378,spiaccicare,3,2 +4379,tangy,3,1 +4380,pungente,3,2 +4381,thicket,3,1 +4382,boschetto,3,2 +4383,toothpick,3,1 +4384,stuzzicadente,3,2 +4385,warden,3,1 +4386,guardiano,3,2 +4387,wayward,3,1 +4388,capriccioso,3,2 +4389,wedge,3,1 +4390,cuneo,3,2 +4391,wistful,3,1 +4392,malinconico,3,2 +4393,brooch,3,1 +4394,spilla,3,2 +4395,chirp,3,1 +4396,cinguettio,3,2 +4397,chisel,3,1 +4398,scalpello,3,2 +4399,frenzy,3,1 +4400,frenesia,3,2 +4401,glaze,3,1 +4402,glassa,3,2 +4403,groove,3,1 +4404,scanalatura,3,2 +4405,leery,3,1 +4406,sospettoso,3,2 +4407,molten,3,1 +4408,fuso,3,2 +4409,mourn,3,1 +4410,compiangere,3,2 +4411,onlooker,3,1 +4412,spettatore,3,2 +4413,slab,3,1 +4414,lastra,3,2 +4415,soot,3,1 +4416,fuliggine,3,2 +4417,squirm,3,1 +4418,dimenarsi,3,2 +4419,stool,3,1 +4420,sgabello,3,2 +4421,stride,3,1 +4422,falcata,3,2 +4423,tiara,3,1 +4424,diadema,3,2 +4425,wanton,3,1 +4426,ingiustificato,3,2 +4427,whinge,3,1 +4428,frignare,3,2 +4429,whorl,3,1 +4430,spirale,3,2 +4431,wiry,3,1 +4432,scolpito,3,2 +4433,appendage,3,1 +4434,appendice,3,2 +4435,badger,3,1 +4436,tasso,3,2 +4437,bramble,3,1 +4438,rovo,3,2 +4439,cap,3,1 +4440,berretto,3,2 +4441,chalk,3,1 +4442,gesso,3,2 +4443,dagger,3,1 +4444,pugnale,3,2 +4445,doily,3,1 +4446,centrino,3,2 +4447,faze,3,1 +4448,turbare,3,2 +4449,grave,3,1 +4450,tomba,3,2 +4451,leech,3,1 +4452,sanguisuga,3,2 +4453,mat,3,1 +4454,tappetino,3,2 +4455,moisture,3,1 +4456,umidita',3,2 +4457,pitcher,3,1 +4458,brocca,3,2 +4459,riffraff,3,1 +4460,gentaglia,3,2 +4461,scuff,3,1 +4462,strascicare,3,2 +4463,shrivel,3,1 +4464,avvizzire,3,2 +4465,turnip,3,1 +4466,rapa,3,2 +4467,upstart,3,1 +4468,arrivista,3,2 +4469,wisp,3,1 +4470,ciocca,3,2 +4471,wondrous,3,1 +4472,meraviglioso,3,2 +4473,accolade,3,1 +4474,riconoscimento,3,2 +4475,ache,3,1 +4476,dolore,3,2 +4477,brandish,3,1 +4478,brandire,3,2 +4479,caveat,3,1 +4480,avvertimento,3,2 +4481,deluge,3,1 +4482,diluvio,3,2 +4483,drizzle,3,1 +4484,pioggerella,3,2 +4485,gag,3,1 +4486,bavaglio,3,2 +4487,heap,3,1 +4488,mucchio,3,2 +4489,jettison,3,1 +4490,disfarsi,3,2 +4491,laud,3,1 +4492,lodare,3,2 +4493,lest,3,1 +4494,per paura che,3,2 +4495,lighthearted,3,1 +4496,spensierato,3,2 +4497,lustre,3,1 +4498,lucentezza,3,2 +4499,maelstrom,3,1 +4500,vortice,3,2 +4501,posit,3,1 +4502,postulare,3,2 +4503,ream,3,1 +4504,risma,3,2 +4505,relinquish,3,1 +4506,abbandonare,3,2 +4507,seclusion,3,1 +4508,solitudine,3,2 +4509,whirlpool,3,1 +4510,mulinello,3,2 +4511,zeal,3,1 +4512,zelo,3,2 +4513,adage,3,1 +4514,detto,3,2 +4515,chill,3,1 +4516,raffreddare,3,2 +4517,entrench,3,1 +4518,consolidare,3,2 +4519,fester,3,1 +4520,infettare,3,2 +4521,flagship,3,1 +4522,ammiraglia,3,2 +4523,freight,3,1 +4524,merci,3,2 +4525,gooey,3,1 +4526,appiccicoso,3,2 +4527,gruelling,3,1 +4528,estenuante,3,2 +4529,kudos,3,1 +4530,gloria,3,2 +4531,lacklustre,3,1 +4532,scialbo,3,2 +4533,liken,3,1 +4534,paragonare,3,2 +4535,mercurial,3,1 +4536,mutevole,3,2 +4537,rebound,3,1 +4538,ripresa,3,2 +4539,revel,3,1 +4540,baldoria,3,2 +4541,satiate,3,1 +4542,saziare,3,2 +4543,standout,3,1 +4544,risaltare,3,2 +4545,trim,3,1 +4546,finitura,3,2 +4547,uproot,3,1 +4548,sradicare,3,2 +4549,wangle,3,1 +4550,procacciare,3,2 +4551,wrangle,3,1 +4552,disputa,3,2 +4553,abject,3,1 +4554,miserabile,3,2 +4555,augur,3,1 +4556,presagire,3,2 +4557,awning,3,1 +4558,veranda,3,2 +4559,babble,3,1 +4560,farfugliare,3,2 +4561,cabin,3,1 +4562,baita,3,2 +4563,cartridge,3,1 +4564,cartuccia,3,2 +4565,classified,3,1 +4566,confidenziale,3,2 +4567,crowbar,3,1 +4568,piede di porco,3,2 +4569,demise,3,1 +4570,decesso,3,2 +4571,dreary,3,1 +4572,tetro,3,2 +4573,duffel,3,1 +4574,borsone,3,2 +4575,martinet,3,1 +4576,despota,3,2 +4577,moot,3,1 +4578,discutibile,3,2 +4579,oar,3,1 +4580,remo,3,2 +4581,precinct,3,1 +4582,distretto,3,2 +4583,pushover,3,1 +4584,sempliciotto,3,2 +4585,site,3,1 +4586,cantiere,3,2 +4587,skillet,3,1 +4588,tegame,3,2 +4589,sleet,3,1 +4590,nevischio,3,2 +4591,tirade,3,1 +4592,invettiva,3,2 +4593,crumb,3,1 +4594,briciola,3,2 +4595,easel,3,1 +4596,cavalletto,3,2 +4597,ebb,3,1 +4598,riflusso,3,2 +4599,enamel,3,1 +4600,smalto,3,2 +4601,encase,3,1 +4602,rivestire,3,2 +4603,encrusted,3,1 +4604,incrostato,3,2 +4605,etch,3,1 +4606,incidere,3,2 +4607,goatee,3,1 +4608,pizzetto,3,2 +4609,goose,3,1 +4610,oca,3,2 +4611,lace,3,1 +4612,merletto,3,2 +4613,listless,3,1 +4614,svogliato,3,2 +4615,mop,3,1 +4616,mocio,3,2 +4617,probation,3,1 +4618,liberta' vigilata,3,2 +4619,shrimp,3,1 +4620,gamberetto,3,2 +4621,shrine,3,1 +4622,santuario,3,2 +4623,snuggle,3,1 +4624,accoccolarsi,3,2 +4625,strainer,3,1 +4626,colino,3,2 +4627,tide,3,1 +4628,marea,3,2 +4629,trailer,3,1 +4630,rimorchio,3,2 +4631,waver,3,1 +4632,titubare,3,2 +4633,acorn,3,1 +4634,ghianda,3,2 +4635,barter,3,1 +4636,permuta,3,2 +4637,copout,3,1 +4638,scappatoia,3,2 +4639,dimple,3,1 +4640,fossetta,3,2 +4641,disheveled,3,1 +4642,scompigliato,3,2 +4643,earnest,3,1 +4644,coscienzioso,3,2 +4645,feral,3,1 +4646,selvatico,3,2 +4647,heirloom,3,1 +4648,cimelio di famiglia,3,2 +4649,oak,3,1 +4650,quercia,3,2 +4651,outrageous,3,1 +4652,oltraggioso,3,2 +4653,pang,3,1 +4654,fitta,3,2 +4655,scour,3,1 +4656,perlustrare,3,2 +4657,sever,3,1 +4658,recidere,3,2 +4659,skylight,3,1 +4660,lucernario,3,2 +4661,sled,3,1 +4662,slitta,3,2 +4663,smoothie,3,1 +4664,frullato,3,2 +4665,speckled,3,1 +4666,maculato,3,2 +4667,stoned,3,1 +4668,sballato,3,2 +4669,tinfoil,3,1 +4670,stagnola,3,2 +4671,wad,3,1 +4672,mazzetta,3,2 +4673,belt,3,1 +4674,cintura,3,2 +4675,bungle,3,1 +4676,pasticciare,3,2 +4677,careful,3,1 +4678,attenzione,3,2 +4679,clamour,3,1 +4680,fragore,3,2 +4681,clue,3,1 +4682,indizio,3,2 +4683,earmark,3,1 +4684,destinare,3,2 +4685,fight,3,1 +4686,combattere,3,2 +4687,hidden,3,1 +4688,nascosto,3,2 +4689,ibex,3,1 +4690,stambecco,3,2 +4691,instead,3,1 +4692,anziche',3,2 +4693,plateau,3,1 +4694,altopiano,3,2 +4695,pothole,3,1 +4696,buca,3,2 +4697,reap,3,1 +4698,raccogliere,3,2 +4699,refurbish,3,1 +4700,ristrutturare,3,2 +4701,shout,3,1 +4702,gridare,3,2 +4703,steal,3,1 +4704,rubare,3,2 +4705,sunlit,3,1 +4706,soleggiato,3,2 +4707,trove,3,1 +4708,collezione,3,2 +4709,wake,3,1 +4710,svegliare,3,2 +4711,whimsy,3,1 +4712,fantasia,3,2 +4713,barely,3,1 +4714,a malapena,3,2 +4715,bill,3,1 +4716,conto,3,2 +4717,boon,3,1 +4718,beneficio,3,2 +4719,budding,3,1 +4720,in erba,3,2 +4721,cloudburst,3,1 +4722,nubifragio,3,2 +4723,creep in,3,1 +4724,insinuarsi,3,2 +4725,fade,3,1 +4726,svanire,3,2 +4727,fiat,3,1 +4728,decreto,3,2 +4729,field,3,1 +4730,campo,3,2 +4731,goad,3,1 +4732,pungolare,3,2 +4733,headland,3,1 +4734,promontorio,3,2 +4735,hill,3,1 +4736,collina,3,2 +4737,jinx,3,1 +4738,iella,3,2 +4739,legerdemain,3,1 +4740,gioco di prestigio,3,2 +4741,lineage,3,1 +4742,lignaggio,3,2 +4743,mold,3,1 +4744,muffa,3,2 +4745,slice,3,1 +4746,fetta,3,2 +4747,vanguard,3,1 +4748,avanguardia,3,2 +4749,varnish,3,1 +4750,vernice,3,2 +4751,zinger,3,1 +4752,frecciatina,3,2 +4753,allow,3,1 +4754,permettere,3,2 +4755,attune,3,1 +4756,in sintonia,3,2 +4757,awful,3,1 +4758,tremendo,3,2 +4759,cheap,3,1 +4760,economico,3,2 +4761,daydream,3,1 +4762,fantasticare,3,2 +4763,deaf,3,1 +4764,sordo,3,2 +4765,dive,3,1 +4766,tuffarsi,3,2 +4767,dustbin,3,1 +4768,pattumiera,3,2 +4769,fireplace,3,1 +4770,camino,3,2 +4771,grapple,3,1 +4772,essere alle prese,3,2 +4773,greengrocer,3,1 +4774,fruttivendolo,3,2 +4775,longing,3,1 +4776,nostalgia,3,2 +4777,moniker,3,1 +4778,appellativo,3,2 +4779,nuptials,3,1 +4780,nozze,3,2 +4781,parrot,3,1 +4782,pappagallo,3,2 +4783,perforce,3,1 +4784,necessariamente,3,2 +4785,seedling,3,1 +4786,piantina,3,2 +4787,straggler,3,1 +4788,fanalino di coda,3,2 +4789,tier,3,1 +4790,strato,3,2 +4791,watchword,3,1 +4792,motto,3,2 +4793,barn,3,1 +4794,stalla,3,2 +4795,bone,3,1 +4796,osso,3,2 +4797,calf,3,1 +4798,polpaccio,3,2 +4799,gamut,3,1 +4800,gamma,3,2 +4801,hereto,3,1 +4802,al presente,3,2 +4803,heretofore,3,1 +4804,fino ad allora,3,2 +4805,in a jiffy,3,1 +4806,in un attimo,3,2 +4807,joint,3,1 +4808,articolazione,3,2 +4809,lazy,3,1 +4810,pigro,3,2 +4811,mete out,3,1 +4812,infliggere,3,2 +4813,porter,3,1 +4814,facchino,3,2 +4815,proselytise,3,1 +4816,convertire,3,2 +4817,rebuttal,3,1 +4818,confutazione,3,2 +4819,stamp,3,1 +4820,francobollo,3,2 +4821,stark,3,1 +4822,assoluto,3,2 +4823,stronghold,3,1 +4824,roccaforte,3,2 +4825,stymie,3,1 +4826,boicottare,3,2 +4827,sweat,3,1 +4828,sudore,3,2 +4829,twin,3,1 +4830,gemello,3,2 +4831,yard,3,1 +4832,cortile,3,2 +4833,alas,3,1 +4834,ahime',3,2 +4835,augment,3,1 +4836,accrescere,3,2 +4837,depth,3,1 +4838,profondita',3,2 +4839,dole,3,1 +4840,sussidio,3,2 +4841,galore,3,1 +4842,in abbondanza,3,2 +4843,goat,3,1 +4844,capra,3,2 +4845,hang,3,1 +4846,appendere,3,2 +4847,mincing,3,1 +4848,lezioso,3,2 +4849,orchard,3,1 +4850,frutteto,3,2 +4851,perched,3,1 +4852,arroccato,3,2 +4853,proviso,3,1 +4854,condizione,3,2 +4855,ribbon,3,1 +4856,nastro,3,2 +4857,shelve,3,1 +4858,accantonare,3,2 +4859,shirk,3,1 +4860,sottrarsi,3,2 +4861,squib,3,1 +4862,petardo,3,2 +4863,supersede,3,1 +4864,rimpiazzare,3,2 +4865,tune,3,1 +4866,sintonizzare,3,2 +4867,uproar,3,1 +4868,tumulto,3,2 +4869,uptake,3,1 +4870,assorbimento,3,2 +4871,wise,3,1 +4872,saggio,3,2 +4873,celery,3,1 +4874,sedano,3,2 +4875,clad,3,1 +4876,rivestito,3,2 +4877,clever,3,1 +4878,perspicace,3,2 +4879,dispel,3,1 +4880,dissipare,3,2 +4881,foist on,3,1 +4882,rifilare,3,2 +4883,forbear,3,1 +4884,astenersi,3,2 +4885,forsake,3,1 +4886,lasciare,3,2 +4887,gathering,3,1 +4888,radunare,3,2 +4889,gloaming,3,1 +4890,imbrunire,3,2 +4891,headstrong,3,1 +4892,caparbio,3,2 +4893,inchoate,3,1 +4894,incipiente,3,2 +4895,nod,3,1 +4896,cenno del capo,3,2 +4897,seek,3,1 +4898,cercare di,3,2 +4899,setback,3,1 +4900,battuta d'arresto,3,2 +4901,sparrow,3,1 +4902,passero,3,2 +4903,stoked,3,1 +4904,entusiasta,3,2 +4905,strew,3,1 +4906,cospargere,3,2 +4907,surmount,3,1 +4908,sormontare,3,2 +4909,undertake,3,1 +4910,intraprendere,3,2 +4911,unwitting,3,1 +4912,inconsapevole,3,2 +4913,adjoined,3,1 +4914,annesso,3,2 +4915,cog,3,1 +4916,ingranaggio,3,2 +4917,covert,3,1 +4918,sotto copertura,3,2 +4919,earshot,3,1 +4920,portata d'orecchio,3,2 +4921,hourglass,3,1 +4922,clessidra,3,2 +4923,knuckle,3,1 +4924,nocca,3,2 +4925,littered,3,1 +4926,disseminato,3,2 +4927,mash,3,1 +4928,purea,3,2 +4929,mirth,3,1 +4930,ilarita',3,2 +4931,peacock,3,1 +4932,pavone,3,2 +4933,pigeon,3,1 +4934,piccione,3,2 +4935,rejoice,3,1 +4936,rallegrarsi,3,2 +4937,selfish,3,1 +4938,egoista,3,2 +4939,spat,3,1 +4940,screzio,3,2 +4941,spelt,3,1 +4942,farro,3,2 +4943,strut,3,1 +4944,pavoneggiarsi,3,2 +4945,survey,3,1 +4946,indagine,3,2 +4947,swivel chair,3,1 +4948,sedia girevole,3,2 +4949,wig,3,1 +4950,parrucca,3,2 +4951,windmill,3,1 +4952,mulino a vento,3,2 +4953,achieve,3,1 +4954,realizzare,3,2 +4955,acquaintance,3,1 +4956,conoscente,3,2 +4957,bench,3,1 +4958,panca,3,2 +4959,captive,3,1 +4960,prigioniero,3,2 +4961,ceiling,3,1 +4962,soffitto,3,2 +4963,deference,3,1 +4964,deferenza,3,2 +4965,deflate,3,1 +4966,sgonfiare,3,2 +4967,demeaning,3,1 +4968,umiliante,3,2 +4969,entrepreneur,3,1 +4970,imprenditore,3,2 +4971,feed,3,1 +4972,dare da mangiare,3,2 +4973,gurgle,3,1 +4974,gorgogliare,3,2 +4975,lacquer,3,1 +4976,lacca,3,2 +4977,maw,3,1 +4978,fauci,3,2 +4979,mushroom,3,1 +4980,fungo,3,2 +4981,naysayer,3,1 +4982,disfattista,3,2 +4983,pineapple,3,1 +4984,ananas,3,2 +4985,requite,3,1 +4986,contraccambiare,3,2 +4987,rind,3,1 +4988,scorza,3,2 +4989,splotch,3,1 +4990,chiazza,3,2 +4991,stick up for,3,1 +4992,supportare,3,2 +4993,accordion,3,1 +4994,fisarmonica,3,2 +4995,bonfire,3,1 +4996,falo',3,2 +4997,contrite,3,1 +4998,mortificato,3,2 +4999,coo,3,1 +5000,tubare,3,2 +5001,harm,3,1 +5002,danno,3,2 +5003,hoof,3,1 +5004,zoccolo,3,2 +5005,improve,3,1 +5006,migliorare,3,2 +5007,knob,3,1 +5008,pomello,3,2 +5009,mane,3,1 +5010,criniera,3,2 +5011,padded,3,1 +5012,imbottito,3,2 +5013,parch,3,1 +5014,inaridire,3,2 +5015,quibble,3,1 +5016,cavillo,3,2 +5017,rib,3,1 +5018,costa,3,2 +5019,rubble,3,1 +5020,macerie,3,2 +5021,shoulder blade,3,1 +5022,scapola,3,2 +5023,stub,3,1 +5024,mozzicone,3,2 +5025,tuft,3,1 +5026,ciuffo,3,2 +5027,twinkle,3,1 +5028,scintillare,3,2 +5029,whinny,3,1 +5030,nitrito,3,2 +5031,whip,3,1 +5032,frusta,3,2 +5033,beautician,3,1 +5034,estetista,3,2 +5035,chestnut,3,1 +5036,castagna,3,2 +5037,donkey,3,1 +5038,asino,3,2 +5039,flax,3,1 +5040,lino,3,2 +5041,inlay,3,1 +5042,intarsio,3,2 +5043,ladybird,3,1 +5044,coccinella,3,2 +5045,masquerade,3,1 +5046,messa in scena,3,2 +5047,meadow,3,1 +5048,prateria,3,2 +5049,mellow,3,1 +5050,tranquillo,3,2 +5051,phlegm,3,1 +5052,catarro,3,2 +5053,piffle,3,1 +5054,stupidaggini,3,2 +5055,shareholder,3,1 +5056,socio,3,2 +5057,skull,3,1 +5058,cranio,3,2 +5059,snout,3,1 +5060,muso,3,2 +5061,surgeon,3,1 +5062,chirurgo,3,2 +5063,timeworn,3,1 +5064,logoro,3,2 +5065,turkey,3,1 +5066,tacchino,3,2 +5067,tusk,3,1 +5068,zanna,3,2 +5069,untenable,3,1 +5070,insostenibile,3,2 +5071,yawn,3,1 +5072,sbadigliare,3,2 +5073,plaster,3,1 +5074,intonaco,3,2 +5075,hut,3,1 +5076,capanna,3,2 +5077,greet,3,1 +5078,accogliere,3,2 +5079,finery,3,1 +5080,abiti eleganti,3,2 +5081,gape,3,1 +5082,a bocca aperta,3,2 +5083,rod,3,1 +5084,asta,3,2 +5085,brass,3,1 +5086,ottone,3,2 +5087,demote,3,1 +5088,degradare,3,2 +5089,unbecoming,3,1 +5090,indecoroso,3,2 +5091,mob,3,1 +5092,orda,3,2 +5093,dragonfly,3,1 +5094,libellula,3,2 +5095,browse,3,1 +5096,sfogliare,3,2 +5097,superimpose,3,1 +5098,sovrapporre,3,2 +5099,squeeze,3,1 +5100,spremere,3,2 +5101,struggle,3,1 +5102,lotta,3,2 +5103,for the sake of,3,1 +5104,per il bene di,3,2 +5105,sidelong,3,1 +5106,di traverso,3,2 +5107,stack,3,1 +5108,pila,3,2 +5109,elm,3,1 +5110,olmo,3,2 +5111,slink,3,1 +5112,sgattaiolare,3,2 +5113,arsonist,3,1 +5114,piromane,3,2 +5115,blaze,3,1 +5116,vampata,3,2 +5117,blind,3,1 +5118,cieco,3,2 +5119,burgeon,3,1 +5120,svilupparsi,3,2 +5121,destitute,3,1 +5122,indigente,3,2 +5123,feel like,3,1 +5124,sentire come,3,2 +5125,fill,3,1 +5126,riempire,3,2 +5127,fingertip,3,1 +5128,polpastrello,3,2 +5129,gripping,3,1 +5130,avvincente,3,2 +5131,hawker,3,1 +5132,venditore ambulante,3,2 +5133,kneecap,3,1 +5134,rotula,3,2 +5135,lattice,3,1 +5136,reticolo,3,2 +5137,lie,3,1 +5138,bugia,3,2 +5139,mollify,3,1 +5140,quietare,3,2 +5141,phoenix,3,1 +5142,fenice,3,2 +5143,plush,3,1 +5144,peluche,3,2 +5145,riveting,3,1 +5146,affascinante,3,2 +5147,scan,3,1 +5148,scandire,3,2 +5149,verbatim,3,1 +5150,parola per parola,3,2 +5151,within,3,1 +5152,all'interno,3,2 +5153,blackmail,3,1 +5154,ricatto,3,2 +5155,call it a day,3,1 +5156,per oggi basta cosi',3,2 +5157,face it,3,1 +5158,ammettilo,3,2 +5159,fissure,3,1 +5160,spaccatura,3,2 +5161,foreshadow,3,1 +5162,prefigurare,3,2 +5163,gorge,3,1 +5164,gola,3,2 +5165,hideous,3,1 +5166,orrendo,3,2 +5167,leisure,3,1 +5168,tempo libero,3,2 +5169,make a beeline for,3,1 +5170,puntare dritto a,3,2 +5171,overfly,3,1 +5172,sorvolare,3,2 +5173,overwhelming,3,1 +5174,opprimente,3,2 +5175,pistachio,3,1 +5176,pistacchio,3,2 +5177,plough,3,1 +5178,aratro,3,2 +5179,rather,3,1 +5180,piuttosto,3,2 +5181,recluse,3,1 +5182,eremita,3,2 +5183,seismic,3,1 +5184,sismico,3,2 +5185,siphon,3,1 +5186,sifone,3,2 +5187,understate,3,1 +5188,sottostimare,3,2 +5189,whitewash,3,1 +5190,imbiancare,3,2 +5191,wiretap,3,1 +5192,intercettare,3,2 +5193,aloud,3,1 +5194,a voce alta,3,2 +5195,asbestos,3,1 +5196,amianto,3,2 +5197,astonishing,3,1 +5198,stupefacente,3,2 +5199,beforehand,3,1 +5200,in anticipo,3,2 +5201,beset,3,1 +5202,circondato da,3,2 +5203,county,3,1 +5204,contea,3,2 +5205,draconian,3,1 +5206,draconiano,3,2 +5207,drawback,3,1 +5208,inconveniente,3,2 +5209,envoy,3,1 +5210,emissario,3,2 +5211,idle,3,1 +5212,inattivo,3,2 +5213,liaise,3,1 +5214,cooperare,3,2 +5215,litigation,3,1 +5216,contenzioso,3,2 +5217,maritime,3,1 +5218,marittimo,3,2 +5219,pain,3,1 +5220,dolore fisico,3,2 +5221,rile,3,1 +5222,seccare,3,2 +5223,spearhead,3,1 +5224,capeggiare,3,2 +5225,stalk,3,1 +5226,gambo,3,2 +5227,tiny,3,1 +5228,minuscolo,3,2 +5229,waste,3,1 +5230,spreco,3,2 +5231,whisper,3,1 +5232,sussurrare,3,2 +5233,abuzz,3,1 +5234,in fermento,3,2 +5235,bravery,3,1 +5236,coraggio,3,2 +5237,cornerstone,3,1 +5238,pietra angolare,3,2 +5239,dig,3,1 +5240,scavare,3,2 +5241,fitted with,3,1 +5242,munito,3,2 +5243,mankind,3,1 +5244,genere umano,3,2 +5245,mince,3,1 +5246,tritare,3,2 +5247,no brainer,3,1 +5248,scelta ovvia,3,2 +5249,quote,3,1 +5250,citazione,3,2 +5251,resin,3,1 +5252,resina,3,2 +5253,runoff,3,1 +5254,ballottaggio,3,2 +5255,shed,3,1 +5256,capanno,3,2 +5257,squid,3,1 +5258,calamaro,3,2 +5259,tenet,3,1 +5260,fondamento,3,2 +5261,thickness,3,1 +5262,spessore,3,2 +5263,threaten,3,1 +5264,minacciare,3,2 +5265,tiebreaker,3,1 +5266,spareggio,3,2 +5267,timber,3,1 +5268,legname,3,2 +5269,wade,3,1 +5270,guadare,3,2 +5271,weed,3,1 +5272,diserbare,3,2 +5273,bride,3,1 +5274,sposa,3,2 +5275,denial,3,1 +5276,negazione,3,2 +5277,goal,3,1 +5278,obiettivo,3,2 +5279,gull,3,1 +5280,gabbiano,3,2 +5281,heron,3,1 +5282,airone,3,2 +5283,innermost,3,1 +5284,piu' interno,3,2 +5285,intersperse,3,1 +5286,inframezzare,3,2 +5287,masterpiece,3,1 +5288,capolavoro,3,2 +5289,pale,3,1 +5290,pallido,3,2 +5291,riot,3,1 +5292,rivolta,3,2 +5293,shaggy,3,1 +5294,arruffato,3,2 +5295,snowplow,3,1 +5296,spazzaneve,3,2 +5297,spellbound,3,1 +5298,incantato,3,2 +5299,stowaway,3,1 +5300,clandestino,3,2 +5301,stranded,3,1 +5302,bloccato,3,2 +5303,suitable,3,1 +5304,idoneo,3,2 +5305,tarmac,3,1 +5306,asfalto,3,2 +5307,taste,3,1 +5308,assaggio,3,2 +5309,tuxedo,3,1 +5310,smoking,3,2 +5311,worthy,3,1 +5312,degno,3,2 +5313,abet,3,1 +5314,favoreggiare,3,2 +5315,bowel,3,1 +5316,intestino,3,2 +5317,chime,3,1 +5318,rintoccare,3,2 +5319,darn,3,1 +5320,rammendare,3,2 +5321,deer,3,1 +5322,cervo,3,2 +5323,dwarf,3,1 +5324,nano,3,2 +5325,embezzle,3,1 +5326,intascare,3,2 +5327,flask,3,1 +5328,thermos,3,2 +5329,grab,3,1 +5330,agguantare,3,2 +5331,groom,3,1 +5332,sposo,3,2 +5333,haggard,3,1 +5334,sparuto,3,2 +5335,ivy,3,1 +5336,edera,3,2 +5337,kite,3,1 +5338,aquilone,3,2 +5339,miser,3,1 +5340,avaro,3,2 +5341,needle,3,1 +5342,ago,3,2 +5343,owl,3,1 +5344,gufo,3,2 +5345,parley,3,1 +5346,trattativa,3,2 +5347,propeller,3,1 +5348,elica,3,2 +5349,purr,3,1 +5350,fare le fusa,3,2 +5351,swap,3,1 +5352,scambiare,3,2 +5353,aft,3,1 +5354,a poppa,3,2 +5355,chainsaw,3,1 +5356,motosega,3,2 +5357,chimney,3,1 +5358,comignolo,3,2 +5359,countermand,3,1 +5360,revocare,3,2 +5361,crochet,3,1 +5362,uncinetto,3,2 +5363,cuckoo,3,1 +5364,cuculo,3,2 +5365,deckhand,3,1 +5366,mozzo,3,2 +5367,disrepute,3,1 +5368,cattiva fama,3,2 +5369,downfall,3,1 +5370,caduta dal potere,3,2 +5371,geld,3,1 +5372,castrare,3,2 +5373,giggle,3,1 +5374,ridacchiare,3,2 +5375,helm,3,1 +5376,timone,3,2 +5377,magpie,3,1 +5378,gazza,3,2 +5379,oyster,3,1 +5380,ostrica,3,2 +5381,robin,3,1 +5382,pettirosso,3,2 +5383,skein,3,1 +5384,matassa,3,2 +5385,squirrel,3,1 +5386,scoiattolo,3,2 +5387,standoffish,3,1 +5388,scostante,3,2 +5389,tadpole,3,1 +5390,girino,3,2 +5391,tapered,3,1 +5392,affusolato,3,2 +5393,aware,3,1 +5394,conscio,3,2 +5395,backbiting,3,1 +5396,maldicenza,3,2 +5397,bad omen,3,1 +5398,cattivo presagio,3,2 +5399,brawl,3,1 +5400,rissa,3,2 +5401,covenant,3,1 +5402,patto,3,2 +5403,eel,3,1 +5404,anguilla,3,2 +5405,effrontery,3,1 +5406,sfrontatezza,3,2 +5407,garish,3,1 +5408,sgargiante,3,2 +5409,in abeyance,3,1 +5410,in sospeso,3,2 +5411,on average,3,1 +5412,nella media,3,2 +5413,profligate,3,1 +5414,dissoluto,3,2 +5415,pumpkin,3,1 +5416,zucca,3,2 +5417,raft,3,1 +5418,zattera,3,2 +5419,seat belt,3,1 +5420,cintura di sicurezza,3,2 +5421,sentient,3,1 +5422,senziente,3,2 +5423,shiver,3,1 +5424,rabbrividire,3,2 +5425,snail,3,1 +5426,lumaca,3,2 +5427,transient,3,1 +5428,transitorio,3,2 +5429,uneven,3,1 +5430,irregolare,3,2 +5431,well,3,1 +5432,pozzo,3,2 +5433,accomplice,3,1 +5434,complice,3,2 +5435,awesome,3,1 +5436,fantastico,3,2 +5437,bottomless,3,1 +5438,senza fondo,3,2 +5439,cheat,3,1 +5440,barare,3,2 +5441,craft,3,1 +5442,mestiere,3,2 +5443,fiend,3,1 +5444,demonio,3,2 +5445,heathen,3,1 +5446,pagano,3,2 +5447,jaunt,3,1 +5448,gita,3,2 +5449,lily,3,1 +5450,giglio,3,2 +5451,linen,3,1 +5452,biancheria,3,2 +5453,mason,3,1 +5454,muratore,3,2 +5455,nettle,3,1 +5456,ortica,3,2 +5457,pit,3,1 +5458,fossa,3,2 +5459,pity,3,1 +5460,compatire,3,2 +5461,scoot over,3,1 +5462,fatti in la,3,2 +5463,scroll,3,1 +5464,pergamena,3,2 +5465,sieve,3,1 +5466,setaccio,3,2 +5467,spread,3,1 +5468,diffondersi,3,2 +5469,stage,3,1 +5470,palco,3,2 +5471,whenever,3,1 +5472,ogni volta,3,2 +5473,castaway,3,1 +5474,naufrago,3,2 +5475,chauffeur,3,1 +5476,autista,3,2 +5477,construe,3,1 +5478,interpretare,3,2 +5479,covetous,3,1 +5480,cupidigia,3,2 +5481,entangled,3,1 +5482,ingarbugliato,3,2 +5483,forefather,3,1 +5484,antenato,3,2 +5485,gasket,3,1 +5486,guarnizione,3,2 +5487,ignite,3,1 +5488,prendere fuoco,3,2 +5489,lean,3,1 +5490,appoggiarsi,3,2 +5491,malfeasance,3,1 +5492,illecito,3,2 +5493,misshapen,3,1 +5494,deforme,3,2 +5495,rapture,3,1 +5496,estasi,3,2 +5497,shake,3,1 +5498,agitare,3,2 +5499,shape,3,1 +5500,forma,3,2 +5501,stakes,3,1 +5502,posta in gioco,3,2 +5503,strict,3,1 +5504,rigoroso,3,2 +5505,switchback,3,1 +5506,tornante,3,2 +5507,teetotal,3,1 +5508,astemio,3,2 +5509,threadbare,3,1 +5510,liso,3,2 +5511,willing,3,1 +5512,disposto,3,2 +5513,abysmal,3,1 +5514,abissale,3,2 +5515,ant,3,1 +5516,formica,3,2 +5517,brawny,3,1 +5518,muscoloso,3,2 +5519,damping,3,1 +5520,smorzamento,3,2 +5521,den,3,1 +5522,covo,3,2 +5523,dote on,3,1 +5524,stravedere per,3,2 +5525,droves,3,1 +5526,frotta,3,2 +5527,each other,3,1 +5528,a vicenda,3,2 +5529,enlightenment,3,1 +5530,illuminazione,3,2 +5531,frost,3,1 +5532,brina,3,2 +5533,headwind,3,1 +5534,vento contrario,3,2 +5535,low blow,3,1 +5536,colpo basso,3,2 +5537,roughly,3,1 +5538,all'incirca,3,2 +5539,safecracker,3,1 +5540,scassinatore,3,2 +5541,simmer,3,1 +5542,sobbollire,3,2 +5543,snarky,3,1 +5544,irreverente,3,2 +5545,sobering,3,1 +5546,che fa riflettere,3,2 +5547,strenuous,3,1 +5548,faticoso,3,2 +5549,therefore,3,1 +5550,percio',3,2 +5551,unassailable,3,1 +5552,inattaccabile,3,2 +5555,accustomed,3,1 +5556,avvezzo,3,2 +5557,atone,3,1 +5558,espiare,3,2 +5559,bright,3,1 +5560,luminoso,3,2 +5561,clockwise,3,1 +5562,in senso orario,3,2 +5563,cognizant,3,1 +5564,essere al corrente,3,2 +5565,concierge,3,1 +5566,portinaio,3,2 +5567,copycat,3,1 +5568,copione,3,2 +5569,dilute,3,1 +5570,diluire,3,2 +5571,endure,3,1 +5572,sopportare,3,2 +5573,join,3,1 +5574,unire,3,2 +5575,matt,3,1 +5576,opaco,3,2 +5577,meantime,3,1 +5578,intanto,3,2 +5579,momentous,3,1 +5580,epocale,3,2 +5581,nest,3,1 +5582,nido,3,2 +5583,pamper,3,1 +5584,viziare,3,2 +5585,pastime,3,1 +5586,passatempo,3,2 +5587,split,3,1 +5588,dividere,3,2 +5589,vilify,3,1 +5590,diffamare,3,2 +5591,watershed,3,1 +5592,spartiacque,3,2 +5593,wound,3,1 +5594,ferita,3,2 +5595,aim,3,1 +5596,mirare,3,2 +5597,bend,3,1 +5598,piegare,3,2 +5599,bishop,3,1 +5600,vescovo,3,2 +5601,blossom,3,1 +5602,fiorire,3,2 +5603,cob,3,1 +5604,pannocchia,3,2 +5605,eager,3,1 +5606,desideroso di,3,2 +5607,fist,3,1 +5608,pugno,3,2 +5609,gnaw,3,1 +5610,rosicchiare,3,2 +5611,hoax,3,1 +5612,bufala,3,2 +5613,insight,3,1 +5614,intuizione,3,2 +5615,leek,3,1 +5616,porro,3,2 +5617,mink,3,1 +5618,visone,3,2 +5619,nape,3,1 +5620,nuca,3,2 +5621,overage,3,1 +5622,eccedenza,3,2 +5623,prig,3,1 +5624,saccente,3,2 +5625,pursue,3,1 +5626,inseguire,3,2 +5627,scoff,3,1 +5628,sbeffeggiare,3,2 +5629,sift,3,1 +5630,setacciare,3,2 +5631,stinger,3,1 +5632,pungiglione,3,2 +5633,unlikely,3,1 +5634,improbabile,3,2 +5635,auctioneer,3,1 +5636,banditore,3,2 +5637,bested,3,1 +5638,avere la meglio,3,2 +5639,bulwark,3,1 +5640,baluardo,3,2 +5641,daisy,3,1 +5642,margherita,3,2 +5643,defeat,3,1 +5644,sconfitta,3,2 +5645,disquiet,3,1 +5646,inquietudine,3,2 +5647,douse,3,1 +5648,gettare acqua su,3,2 +5649,ease,3,1 +5650,attenuare,3,2 +5651,figurehead,3,1 +5652,polena,3,2 +5653,flatter,3,1 +5654,lusingare,3,2 +5655,grasshopper,3,1 +5656,cavalletta,3,2 +5657,habit,3,1 +5658,abitudine,3,2 +5659,prowling,3,1 +5660,aggirarsi,3,2 +5661,roar,3,1 +5662,ruggito,3,2 +5663,rugged,3,1 +5664,accidentato,3,2 +5665,saffron,3,1 +5666,zafferano,3,2 +5667,scab,3,1 +5668,crosta,3,2 +5669,stench,3,1 +5670,tanfo,3,2 +5671,swan,3,1 +5672,cigno,3,2 +5673,yarn,3,1 +5674,filo,3,2 +5675,awl,3,1 +5676,punteruolo,3,2 +5677,basin,3,1 +5678,lavabo,3,2 +5679,blush,3,1 +5680,arrossire,3,2 +5681,braggart,3,1 +5682,sbruffone,3,2 +5683,bric a brac,3,1 +5684,cianfrusaglie,3,2 +5685,dye,3,1 +5686,tintura,3,2 +5687,eke out,3,1 +5688,razionare,3,2 +5689,fatuous,3,1 +5690,fatuo,3,2 +5691,fault,3,1 +5692,guasto,3,2 +5693,foundry,3,1 +5694,fonderia,3,2 +5695,frowning,3,1 +5696,accigliato,3,2 +5697,garland,3,1 +5698,ghirlanda,3,2 +5699,guess,3,1 +5700,indovinare,3,2 +5701,life size,3,1 +5702,grandezza naturale,3,2 +5703,merely,3,1 +5704,semplicemente,3,2 +5705,poaching,3,1 +5706,bracconaggio,3,2 +5707,preen,3,1 +5708,agghindarsi,3,2 +5709,scourge,3,1 +5710,flagellare,3,2 +5711,swallow,3,1 +5712,rondine,3,2 +5713,tilt,3,1 +5714,inclinare,3,2 +5715,bask,3,1 +5716,crogiolarsi,3,2 +5717,borrow,3,1 +5718,prendere in prestito,3,2 +5719,bump into,3,1 +5720,imbattersi,3,2 +5721,burly,3,1 +5722,corpulento,3,2 +5723,canvas,3,1 +5724,tela,3,2 +5725,challenging,3,1 +5726,impegnativo,3,2 +5727,contrive,3,1 +5728,escogitare,3,2 +5729,cringe,3,1 +5730,farsi piccolo,3,2 +5731,croon,3,1 +5732,canticchiare,3,2 +5733,cursory,3,1 +5734,frettoloso,3,2 +5735,dread,3,1 +5736,timore,3,2 +5737,exult,3,1 +5738,esultare,3,2 +5739,faint,3,1 +5740,tenue,3,2 +5741,flaky,3,1 +5742,friabile,3,2 +5743,get rid of,3,1 +5744,sbarazzarsi di,3,2 +5745,invoice,3,1 +5746,fattura,3,2 +5747,nicety,3,1 +5748,finezza,3,2 +5749,shawl,3,1 +5750,scialle,3,2 +5751,starve,3,1 +5752,morire di fame,3,2 +5753,vat,3,1 +5754,tinozza,3,2 +5755,bud,3,1 +5756,bocciolo,3,2 +5757,chaperone,3,1 +5758,accompagnatore,3,2 +5759,chop,3,1 +5760,spaccare,3,2 +5761,christening,3,1 +5762,battesimo,3,2 +5763,crestfallen,3,1 +5764,avvilito,3,2 +5765,exhaust,3,1 +5766,sfinire,3,2 +5767,footman,3,1 +5768,valletto,3,2 +5769,frentic,3,1 +5770,convulso,3,2 +5771,genuflect,3,1 +5772,genuflettersi,3,2 +5773,pantomime,3,1 +5774,pantomima,3,2 +5775,petitioner,3,1 +5776,richiedente,3,2 +5777,proper,3,1 +5778,corretto,3,2 +5779,rascal,3,1 +5780,birbantello,3,2 +5781,reimburse,3,1 +5782,rimborsare,3,2 +5783,scum,3,1 +5784,feccia,3,2 +5785,shingle,3,1 +5786,tegola,3,2 +5787,steeplechase,3,1 +5788,corsa a ostacoli,3,2 +5789,surreptitious,3,1 +5790,furtivo,3,2 +5791,tarry,3,1 +5792,indugiare,3,2 +5793,treacherous,3,1 +5794,sleale,3,2 +5795,afford,3,1 +5796,permettersi,3,2 +5797,allay,3,1 +5798,acquietare,3,2 +5799,bald,3,1 +5800,calvo,3,2 +5801,bumpkin,3,1 +5802,bifolco,3,2 +5803,carousel,3,1 +5804,giostra,3,2 +5805,dowdy,3,1 +5806,senza stile,3,2 +5807,elope,3,1 +5808,fuga d'amore,3,2 +5809,fawning,3,1 +5810,servile,3,2 +5811,flotsam,3,1 +5812,detriti,3,2 +5813,gild,3,1 +5814,dorare,3,2 +5815,guilt,3,1 +5816,colpa,3,2 +5817,hymn,3,1 +5818,inno,3,2 +5819,mantelpiece,3,1 +5820,mensola del camino,3,2 +5821,nowadays,3,1 +5822,al giorno d'oggi,3,2 +5823,ruckus,3,1 +5824,putiferio,3,2 +5825,shake hands,3,1 +5826,stringere la mano,3,2 +5827,snarl,3,1 +5828,ringhiare,3,2 +5829,tidy,3,1 +5830,ordinato,3,2 +5831,warble,3,1 +5832,gorgheggiare,3,2 +5833,wilt,3,1 +5834,perdere vigore,3,2 +5835,abbey,3,1 +5836,abbazia,3,2 +5837,adorned,3,1 +5838,ornato,3,2 +5839,afferent,3,1 +5840,afferente,3,2 +5841,awkward,3,1 +5842,scomodo,3,2 +5843,cobweb,3,1 +5844,ragnatela,3,2 +5845,commonplace,3,1 +5846,luogo comune,3,2 +5847,concur,3,1 +5848,concordare,3,2 +5849,disappointed,3,1 +5850,deluso,3,2 +5851,doleful,3,1 +5852,addolorato,3,2 +5853,flyer,3,1 +5854,volantino,3,2 +5855,glad,3,1 +5856,lieto,3,2 +5857,hit,3,1 +5858,colpire,3,2 +5859,inane,3,1 +5860,insensato,3,2 +5861,nab,3,1 +5862,acciuffare,3,2 +5863,ravine,3,1 +5864,burrone,3,2 +5865,rub,3,1 +5866,sfregare,3,2 +5867,scratch,3,1 +5868,graffio,3,2 +5869,tight,3,1 +5870,stretto,3,2 +5871,weep,3,1 +5872,pianto,3,2 +5873,wing,3,1 +5874,ala,3,2 +5875,amble,3,1 +5876,ambiare,3,2 +5877,amenable,3,1 +5878,ben disposto,3,2 +5879,beam,3,1 +5880,raggio,3,2 +5881,belong,3,1 +5882,appartenere,3,2 +5883,certitude,3,1 +5884,certezza,3,2 +5885,coil,3,1 +5886,bobina,3,2 +5887,dumb,3,1 +5888,muto,3,2 +5889,fancy,3,1 +5890,di lusso,3,2 +5891,gorgeous,3,1 +5892,splendida,3,2 +5893,greasy,3,1 +5894,unto,3,2 +5895,lack,3,1 +5896,mancanza,3,2 +5897,loyalty,3,1 +5898,lealta',3,2 +5899,relieve the pressure,3,1 +5900,ridurre la pressione,3,2 +5901,ringlet,3,1 +5902,boccolo,3,2 +5903,ruse,3,1 +5904,inganno,3,2 +5905,seer,3,1 +5906,veggente,3,2 +5907,smother,3,1 +5908,asfissiare,3,2 +5909,stance,3,1 +5910,presa di posizione,3,2 +5911,tremble,3,1 +5912,tremolio,3,2 +5913,vexatious,3,1 +5914,vessatorio,3,2 +5915,acknowledge,3,1 +5916,riconoscere,3,2 +5917,attitude,3,1 +5918,attitudine,3,2 +5919,bankruptcy,3,1 +5920,bancarotta,3,2 +5921,blank,3,1 +5922,in bianco,3,2 +5923,bleat,3,1 +5924,belare,3,2 +5925,expatriate,3,1 +5926,espatriato,3,2 +5927,exude,3,1 +5928,trasudare,3,2 +5929,fix,3,1 +5930,sistemare,3,2 +5931,former,3,1 +5932,il precedente,3,2 +5933,green room,3,1 +5934,camerino,3,2 +5935,greenhouse,3,1 +5936,serra,3,2 +5937,harebrained,3,1 +5938,strampalato,3,2 +5939,highlight,3,1 +5940,evidenziare,3,2 +5941,latter,3,1 +5942,quest'ultimo,3,2 +5943,pageant,3,1 +5944,sfilata,3,2 +5945,saunter,3,1 +5946,andatura rilassata,3,2 +5947,semiotics,3,1 +5948,semiotica,3,2 +5949,stuff,3,1 +5950,roba,3,2 +5951,thoroughbred,3,1 +5952,purosangue,3,2 +5953,wrapper,3,1 +5954,involucro,3,2 +5955,cage,3,1 +5956,gabbia,3,2 +5957,cod,3,1 +5958,merluzzo,3,2 +5959,courier,3,1 +5960,corriere,3,2 +5961,crumple,3,1 +5962,stropicciare,3,2 +5963,graft,3,1 +5964,innestare,3,2 +5965,gullet,3,1 +5966,esofago,3,2 +5967,humble,3,1 +5968,umile,3,2 +5969,lynch,3,1 +5970,linciare,3,2 +5971,misunderstand,3,1 +5972,frainteso,3,2 +5973,pane,3,1 +5974,riquadro,3,2 +5975,plate,3,1 +5976,piatto,3,2 +5977,refine,3,1 +5978,rifinire,3,2 +5979,slapdash,3,1 +5980,fatto alla buona,3,2 +5981,snug,3,1 +5982,aderente,3,2 +5983,surge,3,1 +5984,impennata,3,2 +5985,sweep,3,1 +5986,spazzare,3,2 +5987,swelter,3,1 +5988,caldo soffocante,3,2 +5989,thickset,3,1 +5990,tarchiato,3,2 +5991,tossed,3,1 +5992,sballottato,3,2 +5993,wince,3,1 +5994,trasalire,3,2 +5995,blockade,3,1 +5996,embargo,3,2 +5997,cheque,3,1 +5998,assegno,3,2 +5999,cue,3,1 +6000,spunto,3,2 +6001,employee,3,1 +6002,impiegato,3,2 +6003,flickering,3,1 +6004,tremolante,3,2 +6005,furrow,3,1 +6006,solco,3,2 +6007,imperil,3,1 +6008,mettere a rischio,3,2 +6009,muffler,3,1 +6010,silenziatore,3,2 +6011,relief,3,1 +6012,sollievo,3,2 +6013,rooster,3,1 +6014,gallo,3,2 +6015,rough,3,1 +6016,ruvido,3,2 +6017,rowdy,3,1 +6018,turbolento,3,2 +6019,sipping,3,1 +6020,sorseggiare,3,2 +6021,slumber,3,1 +6022,assopirsi,3,2 +6035,absent,3,1 +6036,assente,3,2 +6037,belief,3,1 +6038,credenza,3,2 +6039,cinnamon,3,1 +6040,cannella,3,2 +6041,enthral,3,1 +6042,incollato alla sedia,3,2 +6043,gizmo,3,1 +6044,aggeggio,3,2 +6045,hansom,3,1 +6046,carrozza,3,2 +6047,hawk,3,1 +6048,falco,3,2 +6049,lined,3,1 +6050,foderato,3,2 +6051,lubricate,3,1 +6052,lubrificare,3,2 +6053,mutiny,3,1 +6054,ammutinamento,3,2 +6055,platitude,3,1 +6056,frase fatta,3,2 +6057,rickety,3,1 +6058,pericolante,3,2 +6059,upbringing,3,1 +6060,educazione,3,2 +6061,witty,3,1 +6062,spiritoso,3,2 +6075,bigwig,3,1 +6076,pezzo grosso,3,2 +6077,blur,3,1 +6078,sfocato,3,2 +6079,bonkers,3,1 +6080,fuori di testa,3,2 +6081,erratic,3,1 +6082,erratico,3,2 +6083,flesh,3,1 +6084,carne,3,2 +6085,gloom,3,1 +6086,oscurita',3,2 +6087,hike,3,1 +6088,escursione,3,2 +6089,huge,3,1 +6090,immenso,3,2 +6091,interim,3,1 +6092,provvisorio,3,2 +6093,lampoon,3,1 +6094,satira,3,2 +6095,pursuant to,3,1 +6096,ai sensi di,3,2 +6097,swear,3,1 +6098,giurare,3,2 +6099,trample,3,1 +6100,pestare,3,2 +6101,whistle,3,1 +6102,fischio,3,2 +6115,breadcrumb,3,1 +6116,pangrattato,3,2 +6117,drowsy,3,1 +6118,assonnato,3,2 +6119,fair,3,1 +6120,giusto,3,2 +6121,gumption,3,1 +6122,intraprendenza,3,2 +6123,match,3,1 +6124,abbinato,3,2 +6125,matchstick,3,1 +6126,fiammifero,3,2 +6127,poppies,3,1 +6128,papaveri,3,2 +6129,sob,3,1 +6130,singhiozzare,3,2 +6131,soon,3,1 +6132,presto,3,2 +6133,stun,3,1 +6134,stordire,3,2 +6135,trade,3,1 +6136,commercio,3,2 +6137,turntable,3,1 +6138,giradischi,3,2 +6139,yank,3,1 +6140,strattonare,3,2 +6141,yardstick,3,1 +6142,criterio di paragone,3,2 +6143,brace,3,1 +6144,tutore,3,2 +6145,crease,3,1 +6146,grinza,3,2 +6147,deaden,3,1 +6148,attuire,3,2 +6149,deserve,3,1 +6150,meritare,3,2 +6151,dimly,3,1 +6152,vagamente,3,2 +6153,estrange,3,1 +6154,estraniare,3,2 +6155,gross,3,1 +6156,lordo,3,2 +6157,mackintosh,3,1 +6158,impermeabile,3,2 +6159,moose,3,1 +6160,alce,3,2 +6161,runner up,3,1 +6162,secondo classificato,3,2 +6163,snoop around,3,1 +6164,ficcare il naso,3,2 +6165,sodden,3,1 +6166,fradicio,3,2 +6167,twofer,3,1 +6168,due per uno,3,2 +6169,wide,3,1 +6170,ampio,3,2 +6171,block letters,3,1 +6172,stampatello,3,2 +6173,buttonhole,3,1 +6174,asola,3,2 +6175,coop,3,1 +6176,pollaio,3,2 +6177,gaunt,3,1 +6178,scarno,3,2 +6179,irretrievable,3,1 +6180,irrimediabile,3,2 +6181,knucklehead,3,1 +6182,testone,3,2 +6183,label,3,1 +6184,etichetta,3,2 +6185,lowbrow,3,1 +6186,poco colto,3,2 +6187,mushy,3,1 +6188,sdolcinato,3,2 +6189,scenery,3,1 +6190,panorama,3,2 +6191,smidgen,3,1 +6192,pizzico,3,2 +6193,trivial,3,1 +6194,irrilevante,3,2 +6195,vulture,3,1 +6196,avvoltoio,3,2 +6197,wharf,3,1 +6198,molo,3,2 +6199,anchovy,3,1 +6200,acciuga,3,2 +6201,buddy,3,1 +6202,compare,3,2 +6203,cagey,3,1 +6204,evasivo,3,2 +6205,counsel,3,1 +6206,consigliare,3,2 +6207,dashed,3,1 +6208,tratteggiato,3,2 +6209,enraged,3,1 +6210,infuriato,3,2 +6211,helping,3,1 +6212,porzione,3,2 +6213,histrionic,3,1 +6214,istrionico,3,2 +6215,pace,3,1 +6216,ritmo,3,2 +6217,play truant,3,1 +6218,bigiare,3,2 +6219,starry,3,1 +6220,stellato,3,2 +6221,tramp,3,1 +6222,barbone,3,2 +6223,unassuming,3,1 +6224,senza pretese,3,2 +6225,waylay,3,1 +6226,tendere un agguato,3,2 +6227,aide,3,1 +6228,assistente,3,2 +6229,bite,3,1 +6230,pungere,3,2 +6231,cackle,3,1 +6232,schiamazzo,3,2 +6233,despondent,3,1 +6234,abbattuto,3,2 +6235,dizzy,3,1 +6236,vertigine,3,2 +6237,earn,3,1 +6238,guadagnare,3,2 +6239,laughingstock,3,1 +6240,zimbello,3,2 +6241,peek,3,1 +6242,occhiata veloce,3,2 +6243,peerless,3,1 +6244,senza eguali,3,2 +6245,pilfer,3,1 +6246,sgraffignare,3,2 +6247,prank,3,1 +6248,burla,3,2 +6249,risible,3,1 +6250,risibile,3,2 +6251,sedition,3,1 +6252,sedizione,3,2 +6253,thrash,3,1 +6254,picchiare,3,2 +6255,bray,3,1 +6256,ragliare,3,2 +6257,cliff,3,1 +6258,scogliera,3,2 +6259,curvaceous,3,1 +6260,sinuosa,3,2 +6261,dalliance,3,1 +6262,breve rapporto,3,2 +6263,manly,3,1 +6264,virile,3,2 +6265,mosquito,3,1 +6266,zanzara,3,2 +6267,pipe,3,1 +6268,tubo,3,2 +6269,prissy,3,1 +6270,affettato,3,2 +6271,rag,3,1 +6272,straccio,3,2 +6273,rectitude,3,1 +6274,rettitudine,3,2 +6275,thunderstruck,3,1 +6276,scioccato,3,2 +6277,untie,3,1 +6278,slegare,3,2 +6279,voluptuous,3,1 +6280,voluttuoso,3,2 +6281,wink,3,1 +6282,occhiolino,3,2 +6283,acquit,3,1 +6284,assolvere,3,2 +6285,addendum,3,1 +6286,aggiunta,3,2 +6287,fodder,3,1 +6288,foraggio,3,2 +6289,gown,3,1 +6290,abito da sera,3,2 +6291,guild,3,1 +6292,corporazione,3,2 +6293,hedgehog,3,1 +6294,riccio,3,2 +6295,huckleberry,3,1 +6296,mirtillo,3,2 +6297,maggot,3,1 +6298,larva,3,2 +6299,overlay,3,1 +6300,copertura,3,2 +6301,overload,3,1 +6302,sovraccarico,3,2 +6303,panoply,3,1 +6304,panoplia,3,2 +6305,sticky,3,1 +6306,adesivo,3,2 +6307,thundering,3,1 +6308,fragoroso,3,2 +6309,wasteland,3,1 +6310,terra desolata,3,2 +6311,anoint,3,1 +6312,ungere,3,2 +6313,cough,3,1 +6314,tossire,3,2 +6315,drain,3,1 +6316,drenare,3,2 +6317,garlic,3,1 +6318,aglio,3,2 +6319,hint,3,1 +6320,suggerimento,3,2 +6321,hunker down,3,1 +6322,mettersi sotto,3,2 +6323,offal,3,1 +6324,frattaglie,3,2 +6325,ointment,3,1 +6326,unguento,3,2 +6327,perfunctory,3,1 +6328,sbrigativo,3,2 +6329,sheaf,3,1 +6330,covone,3,2 +6331,stripe,3,1 +6332,striscia,3,2 +6333,swirl,3,1 +6334,vorticare,3,2 +6335,whimsical,3,1 +6336,estroso,3,2 +6337,willow,3,1 +6338,salice,3,2 +6339,afterglow,3,1 +6340,riverbero,3,2 +6341,bait,3,1 +6342,esca,3,2 +6343,clearing,3,1 +6344,radura,3,2 +6345,crafty,3,1 +6346,sornione,3,2 +6347,frazzle,3,1 +6348,esaurimento,3,2 +6349,louse,3,1 +6350,pidocchio,3,2 +6351,misstatement,3,1 +6352,inesattezza,3,2 +6353,mottled,3,1 +6354,screziato,3,2 +6355,racket,3,1 +6356,chiasso,3,2 +6357,raring,3,1 +6358,impaziente,3,2 +6359,stilted,3,1 +6360,pomposo,3,2 +6361,stubble,3,1 +6362,barba corta,3,2 +6363,vial,3,1 +6364,fiala,3,2 +6365,vituperation,3,1 +6366,vituperio,3,2 +6367,abominable,3,1 +6368,abominevole,3,2 +6369,buck,3,1 +6370,dollaro,3,2 +6371,commend,3,1 +6372,elogiare,3,2 +6373,crowded,3,1 +6374,affollato,3,2 +6375,discreet,3,1 +6376,discreto,3,2 +6377,enjoin,3,1 +6378,ingiungere,3,2 +6379,freehand,3,1 +6380,a mano libera,3,2 +6381,limp,3,1 +6382,claudicare,3,2 +6383,out of the blue,3,1 +6384,di punto in bianco,3,2 +6385,pie,3,1 +6386,torta,3,2 +6387,row,3,1 +6388,fila,3,2 +6389,tar,3,1 +6390,catrame,3,2 +6391,tattered,3,1 +6392,sbrindellato,3,2 +6393,unheard,3,1 +6394,inaudito,3,2 +6395,awhile,3,1 +6396,per un po',3,2 +6397,bold,3,1 +6398,audace,3,2 +6399,cheeky,3,1 +6400,sbarazzino,3,2 +6401,cranky,3,1 +6402,irritabile,3,2 +6403,deserter,3,1 +6404,disertore,3,2 +6405,devilment,3,1 +6406,diavoleria,3,2 +6407,imbalance,3,1 +6408,squilibrio,3,2 +6409,masseur,3,1 +6410,massaggiatore,3,2 +6411,peak,3,1 +6412,cima,3,2 +6413,renegade,3,1 +6414,rinnegato,3,2 +6415,sketch,3,1 +6416,schizzo,3,2 +6417,sliver,3,1 +6418,frammento,3,2 +6419,upholster,3,1 +6420,tappezzare,3,2 +6421,width,3,1 +6422,larghezza,3,2 +6423,alluring,3,1 +6424,allettante,3,2 +6425,bead,3,1 +6426,perlina,3,2 +6427,briefcase,3,1 +6428,ventiquattrore,3,2 +6429,brisk,3,1 +6430,svelto,3,2 +6431,broach the subject,3,1 +6432,affrontare l'argomento,3,2 +6433,contemptuous,3,1 +6434,sprezzante,3,2 +6435,guarantor,3,1 +6436,garante,3,2 +6437,nexus,3,1 +6438,nesso,3,2 +6439,scar,3,1 +6440,cicatrice,3,2 +6441,seal,3,1 +6442,foca,3,2 +6443,spice,3,1 +6444,spezia,3,2 +6445,stringy,3,1 +6446,filante,3,2 +6447,unbeatable,3,1 +6448,imbattibile,3,2 +6449,uplifting,3,1 +6450,incoraggiante,3,2 +6451,baseless,3,1 +6452,infondato,3,2 +6453,bevel,3,1 +6454,smussare,3,2 +6455,cursing,3,1 +6456,imprecare,3,2 +6457,demur,3,1 +6458,obiettare,3,2 +6459,flyleaf,3,1 +6460,risguardo,3,2 +6461,gesturing,3,1 +6462,gesticolare,3,2 +6463,inherit,3,1 +6464,ereditare,3,2 +6465,quest,3,1 +6466,ricerca,3,2 +6467,rebuke,3,1 +6468,sgridare,3,2 +6469,sardonic,3,1 +6470,sardonico,3,2 +6471,suborn,3,1 +6472,subornare,3,2 +6473,sunbath,3,1 +6474,prendere il sole,3,2 +6475,sunbed,3,1 +6476,sdraio,3,2 +6477,upset,3,1 +6478,turbato,3,2 +6479,adroit,3,1 +6480,sagace,3,2 +6481,anteroom,3,1 +6482,anticamera,3,2 +6483,buttock,3,1 +6484,natica,3,2 +6485,canteen,3,1 +6486,mensa,3,2 +6487,escarpment,3,1 +6488,scarpata,3,2 +6489,gauze,3,1 +6490,garza,3,2 +6491,handlebar,3,1 +6492,manubrio,3,2 +6493,holy father,3,1 +6494,papa,3,2 +6495,jammed,3,1 +6496,inceppato,3,2 +6497,liver,3,1 +6498,fegato,3,2 +6499,obeisance,3,1 +6500,omaggio,3,2 +6501,pummel,3,1 +6502,prendere a pugni,3,2 +6503,towel,3,1 +6504,asciugamano,3,2 +6505,upright,3,1 +6506,eretto,3,2 +6507,caliper,3,1 +6508,calibro,3,2 +6509,crouch,3,1 +6510,accucciarsi,3,2 +6511,diaper,3,1 +6512,pannolino,3,2 +6513,gully,3,1 +6514,canale,3,2 +6515,lane,3,1 +6516,corsia,3,2 +6517,leniency,3,1 +6518,clemenza,3,2 +6519,oatmeal,3,1 +6520,farina d'avena,3,2 +6521,rack,3,1 +6522,rastrelliera,3,2 +6523,saucer,3,1 +6524,piattino,3,2 +6525,shoplift,3,1 +6526,taccheggiare,3,2 +6527,smudge,3,1 +6528,sbavatura,3,2 +6529,sock,3,1 +6530,calzino,3,2 +6531,warn,3,1 +6532,avvertire,3,2 +6533,wasp,3,1 +6534,vespa,3,2 +6535,afresh,3,1 +6536,da capo,3,2 +6537,blast,3,1 +6538,esplosione,3,2 +6539,bump,3,1 +6540,urto,3,2 +6541,carefree,3,1 +6542,sereno,3,2 +6543,counterfeit,3,1 +6544,contraffatto,3,2 +6545,grub up,3,1 +6546,estirpare,3,2 +6547,neck strap,3,1 +6548,tracolla,3,2 +6549,raisin,3,1 +6550,uvetta,3,2 +6551,smash,3,1 +6552,frantumarsi,3,2 +6553,spoon,3,1 +6554,cucchiaio,3,2 +6555,stocky,3,1 +6556,tozzo,3,2 +6557,trout,3,1 +6558,trota,3,2 +6559,wanly,3,1 +6560,debolmente,3,2 +6561,whilst,3,1 +6562,mentre,3,2 +6563,aboveboard,3,1 +6564,a carte scoperte,3,2 +6565,denouement,3,1 +6566,epilogo,3,2 +6567,disingenuous,3,1 +6568,in malafede,3,2 +6569,eminent,3,1 +6570,eminente,3,2 +6571,heal,3,1 +6572,guarire,3,2 +6573,hypocrite,3,1 +6574,ipocrita,3,2 +6575,incurious,3,1 +6576,privo di curiosita',3,2 +6577,infringement,3,1 +6578,infrazione,3,2 +6579,madhouse,3,1 +6580,manicomio,3,2 +6581,merge,3,1 +6582,fusione,3,2 +6583,plot,3,1 +6584,tema,3,2 +6585,underlie,3,1 +6586,stare alla base,3,2 +6587,underneath,3,1 +6588,al di sotto,3,2 +6589,underpin,3,1 +6590,sorreggere,3,2 +6591,blame,3,1 +6592,biasimare,3,2 +6593,finding,3,1 +6594,scoperta,3,2 +6595,fizzle out,3,1 +6596,affievolirsi,3,2 +6597,flute,3,1 +6598,flauto,3,2 +6599,forestall,3,1 +6600,prevenire,3,2 +6601,impassable,3,1 +6602,intransitabile,3,2 +6603,namely,3,1 +6604,vale a dire,3,2 +6605,newbie,3,1 +6606,principiante,3,2 +6607,puzzling,3,1 +6608,enigmatico,3,2 +6609,rampart,3,1 +6610,bastione,3,2 +6611,sentry,3,1 +6612,sentinella,3,2 +6613,underdog,3,1 +6614,sfavorito,3,2 +6615,unfold,3,1 +6616,svelare,3,2 +6617,urge,3,1 +6618,sollecitare,3,2 +6619,afterthought,3,1 +6620,ripensamento,3,2 +6621,condemn,3,1 +6622,condannare,3,2 +6623,gush,3,1 +6624,sgorgare,3,2 +6625,idiosyncrasy,3,1 +6626,idiosincrasia,3,2 +6627,knee brace,3,1 +6628,ginocchiera,3,2 +6629,overturn,3,1 +6630,ribaltare,3,2 +6631,package,3,1 +6632,pacco,3,2 +6633,profiteer,3,1 +6634,approfittatore,3,2 +6635,scorcher,3,1 +6636,giornata torrida,3,2 +6637,striking,3,1 +6638,impressionante,3,2 +6639,tackle,3,1 +6640,fronteggiare,3,2 +6641,tick,3,1 +6642,zecca,3,2 +6643,unappealing,3,1 +6644,poco invitante,3,2 +6645,uptight,3,1 +6646,nervoso,3,2 +6647,as well as,3,1 +6648,cosi' come,3,2 +6649,broad,3,1 +6650,vasto,3,2 +6651,clockwork,3,1 +6652,orologeria,3,2 +6653,defiant,3,1 +6654,provocatorio,3,2 +6655,featured,3,1 +6656,in evidenza,3,2 +6657,gill,3,1 +6658,branchia,3,2 +6659,hangnail,3,1 +6660,pellicina,3,2 +6661,nun,3,1 +6662,suora,3,2 +6663,skill,3,1 +6664,capacita',3,2 +6665,slurp,3,1 +6666,succhiare,3,2 +6667,stampede,3,1 +6668,fuga precipitosa,3,2 +6669,template,3,1 +6670,schema,3,2 +6671,turnstile,3,1 +6672,tornello,3,2 +6673,wager,3,1 +6674,puntare,3,2 +6675,available,3,1 +6676,disponibile,3,2 +6677,drivel,3,1 +6678,banalita',3,2 +6679,framework,3,1 +6680,struttura,3,2 +6681,goodwill,3,1 +6682,buona volonta',3,2 +6683,levity,3,1 +6684,levita',3,2 +6685,makeover,3,1 +6686,rifacimento,3,2 +6687,overlap,3,1 +6688,sovrapposizione,3,2 +6689,pitchfork,3,1 +6690,forcone,3,2 +6691,powder,3,1 +6692,polvere,3,2 +6693,priest,3,1 +6694,sacerdote,3,2 +6695,sanctimonious,3,1 +6696,bigotto,3,2 +6697,swift,3,1 +6698,rapido,3,2 +6699,untoward,3,1 +6700,spiacevole,3,2 +6701,whatsoever,3,1 +6702,sorta,3,2 +6703,befit,3,1 +6704,addire,3,2 +6705,buoy,3,1 +6706,boa,3,2 +6707,craze,3,1 +6708,moda del momento,3,2 +6709,cross,3,1 +6710,valicare,3,2 +6711,custard,3,1 +6712,crema pasticciera,3,2 +6713,foliage,3,1 +6714,fogliame,3,2 +6715,increase,3,1 +6716,aumentare,3,2 +6717,indulgent,3,1 +6718,indulgente,3,2 +6719,knot,3,1 +6720,nodo,3,2 +6721,phalanx,3,1 +6722,falange,3,2 +6723,shuttle,3,1 +6724,navetta,3,2 +6725,spark,3,1 +6726,scintilla,3,2 +6727,subtlety,3,1 +6728,sottigliezza,3,2 +6729,vapid,3,1 +6730,insulso,3,2 +6731,altercation,3,1 +6732,alterco,3,2 +6733,awash,3,1 +6734,inondato,3,2 +6735,caterpillar,3,1 +6736,bruco,3,2 +6737,chalice,3,1 +6738,calice,3,2 +6739,cuff,3,1 +6740,polsino,3,2 +6741,defiance,3,1 +6742,spregio,3,2 +6743,floodlight,3,1 +6744,riflettore,3,2 +6745,fuel,3,1 +6746,carburante,3,2 +6747,marksman,3,1 +6748,tiratore scelto,3,2 +6749,mezzanine,3,1 +6750,soppalco,3,2 +6751,pillar,3,1 +6752,pilastro,3,2 +6753,ratting,3,1 +6754,fare la spia,3,2 +6755,snicker,3,1 +6756,ridere sotto i baffi,3,2 +6757,whole,3,1 +6758,intero,3,2 +6759,as much as,3,1 +6760,tanto quanto,3,2 +6761,bake,3,1 +6762,infornare,3,2 +6763,encore,3,1 +6764,chiedere il bis,3,2 +6765,escapism,3,1 +6766,escapismo,3,2 +6767,further,3,1 +6768,ulteriore,3,2 +6769,impart,3,1 +6770,impartire,3,2 +6771,loophole,3,1 +6772,appiglio,3,2 +6773,mourning,3,1 +6774,lutto,3,2 +6775,parse,3,1 +6776,analizzare,3,2 +6777,pinch,3,1 +6778,pizzicotto,3,2 +6779,siblings,3,1 +6780,fratelli,3,2 +6781,sordid,3,1 +6782,sordido,3,2 +6783,thrilling,3,1 +6784,entusiasmante,3,2 +6785,wean,3,1 +6786,svezzare,3,2 +6787,ale,3,1 +6788,birra,3,2 +6789,bet,3,1 +6790,scommessa,3,2 +6791,boundary,3,1 +6792,confine,3,2 +6793,conviction,3,1 +6794,convinzione,3,2 +6795,defray,3,1 +6796,sostenere le spese,3,2 +6797,handsomely,3,1 +6798,profumatamente,3,2 +6799,marquee,3,1 +6800,tendone,3,2 +6801,nosedive,3,1 +6802,picchiata,3,2 +6803,outstrip,3,1 +6804,distanziare,3,2 +6805,overview,3,1 +6806,panoramica,3,2 +6807,pollinate,3,1 +6808,impollinare,3,2 +6809,subsidize,3,1 +6810,sovvenzionare,3,2 +6811,weird,3,1 +6812,bizzarro,3,2 +6813,whereas,3,1 +6814,laddove,3,2 +6815,accomplish,3,1 +6816,compiere,3,2 +6817,amplifier,3,1 +6818,amplificatore,3,2 +6819,arbitration,3,1 +6820,arbitrato,3,2 +6821,bit by bit,3,1 +6822,piano piano,3,2 +6823,decay,3,1 +6824,decadimento,3,2 +6825,ennoble,3,1 +6826,nobilitare,3,2 +6827,foothold,3,1 +6828,punto d'appoggio,3,2 +6829,income,3,1 +6830,reddito,3,2 +6831,paste,3,1 +6832,incollare,3,2 +6833,quilt,3,1 +6834,trapunta,3,2 +6835,retool,3,1 +6836,riattrezzare,3,2 +6837,rung,3,1 +6838,piolo,3,2 +6839,tongue in cheek,3,1 +6840,ironicamente,3,2 +6841,wrongdoing,3,1 +6842,misfatto,3,2 +6843,carnation,3,1 +6844,garofano,3,2 +6845,cobbler,3,1 +6846,calzolaio,3,2 +6847,cookbook,3,1 +6848,ricettario,3,2 +6849,cricket,3,1 +6850,grillo,3,2 +6851,dangle,3,1 +6852,penzolare,3,2 +6853,griddle,3,1 +6854,piastra,3,2 +6855,jamb,3,1 +6856,stipite,3,2 +6857,lighter,3,1 +6858,accendino,3,2 +6859,restorative,3,1 +6860,rigenerante,3,2 +6861,skirting,3,1 +6862,zoccolino,3,2 +6863,then again,3,1 +6864,d'altronde,3,2 +6865,topic,3,1 +6866,argomento,3,2 +6867,whetstone,3,1 +6868,cote,3,2 +6869,yet again,3,1 +6870,ancora una volta,3,2 +6871,behold,3,1 +6872,ammirare,3,2 +6873,fulfill,3,1 +6874,adempiere,3,2 +6875,handrail,3,1 +6876,corrimano,3,2 +6877,hatched,3,1 +6878,covato,3,2 +6879,insouciance,3,1 +6880,noncuranza,3,2 +6881,ladder,3,1 +6882,scala,3,2 +6883,lazybones,3,1 +6884,scansafatiche,3,2 +6885,morbid,3,1 +6886,morboso,3,2 +6887,preposterous,3,1 +6888,irragionevole,3,2 +6889,retinue,3,1 +6890,scorta,3,2 +6891,squatter,3,1 +6892,abusivo,3,2 +6893,straightforward,3,1 +6894,diretto,3,2 +6895,trustworthy,3,1 +6896,fidato,3,2 +6897,upturn,3,1 +6898,rialzo,3,2 +6899,adoption,3,1 +6900,adottare,3,2 +6901,at the latest,3,1 +6902,al piu' tardi,3,2 +6903,bounce,3,1 +6904,rimbalzo,3,2 +6905,brink,3,1 +6906,orlo,3,2 +6907,cooker hood,3,1 +6908,cappa,3,2 +6909,deal,3,1 +6910,affare,3,2 +6911,decoction,3,1 +6912,decotto,3,2 +6913,emaciate,3,1 +6914,emaciare,3,2 +6915,gorge yourself,3,1 +6916,ingozzarsi,3,2 +6917,indent,3,1 +6918,rientro,3,2 +6919,lampshade,3,1 +6920,paralume,3,2 +6921,ledge,3,1 +6922,sporgenza,3,2 +6923,on the other hand,3,1 +6924,d'altra parte,3,2 +6925,sputter,3,1 +6926,scoppiettare,3,2 +6927,accuracy,3,1 +6928,accuratezza,3,2 +6929,callow,3,1 +6930,inesperto,3,2 +6931,cove,3,1 +6932,baia,3,2 +6933,crampon,3,1 +6934,rampone,3,2 +6935,crayfish,3,1 +6936,gambero,3,2 +6937,detect,3,1 +6938,rilevare,3,2 +6939,downcast,3,1 +6940,scoraggiato,3,2 +6941,dungeon,3,1 +6942,segreta,3,2 +6943,fillet,3,1 +6944,filetto,3,2 +6945,gift,3,1 +6946,dono,3,2 +6947,gnarled,3,1 +6948,nodoso,3,2 +6949,reluctant,3,1 +6950,riluttante,3,2 +6951,rubbish,3,1 +6952,spazzatura,3,2 +6953,spike,3,1 +6954,spuntone,3,2 +6955,alight,3,1 +6956,in fiamme,3,2 +6957,commoner,3,1 +6958,popolano,3,2 +6959,congenial,3,1 +6960,congeniale,3,2 +6961,curdle,3,1 +6962,cagliare,3,2 +6963,decoy,3,1 +6964,diversivo,3,2 +6965,deliverance,3,1 +6966,liberazione,3,2 +6967,equip,3,1 +6968,equipaggiare,3,2 +6969,exceed,3,1 +6970,eccedere,3,2 +6971,flub,3,1 +6972,fare un pasticcio,3,2 +6973,menial,3,1 +6974,lavoro umile,3,2 +6975,oaf,3,1 +6976,babbeo,3,2 +6977,pile,3,1 +6978,cumulo,3,2 +6979,sustainable,3,1 +6980,sostenibile,3,2 +6981,tussle,3,1 +6982,azzuffarsi,3,2 +6983,amount,3,1 +6984,ammontare,3,2 +6985,anvil,3,1 +6986,incudine,3,2 +6987,bellow,3,1 +6988,muggire,3,2 +6989,chat,3,1 +6990,conversazione,3,2 +6991,crucible,3,1 +6992,crogiolo,3,2 +6993,daily,3,1 +6994,quotidiano,3,2 +6995,drawl,3,1 +6996,parlare strascicato,3,2 +6997,get along,3,1 +6998,andare d'accordo,3,2 +6999,heartfelt,3,1 +7000,di cuore,3,2 +7001,housemaid,3,1 +7002,governante,3,2 +7003,pincushion,3,1 +7004,puntaspilli,3,2 +7005,rafter,3,1 +7006,trave,3,2 +7007,reach,3,1 +7008,giungere,3,2 +7009,recall,3,1 +7010,rievocare,3,2 +7011,buckwheat,3,1 +7012,grano saraceno,3,2 +7013,buttress,3,1 +7014,contrafforte,3,2 +7015,contour,3,1 +7016,sagoma,3,2 +7017,corpse,3,1 +7018,cadavere,3,2 +7019,elder,3,1 +7020,sambuco,3,2 +7021,fanciful,3,1 +7022,fantasioso,3,2 +7023,millet,3,1 +7024,miglio,3,2 +7025,needlework,3,1 +7026,ricamo,3,2 +7027,ore,3,1 +7028,minerale,3,2 +7029,scarcely,3,1 +7030,a stento,3,2 +7031,scree,3,1 +7032,ghiaione,3,2 +7033,speck,3,1 +7034,granello,3,2 +7035,trouble,3,1 +7036,guaio,3,2 +7037,volition,3,1 +7038,volonta',3,2 +7039,airship,3,1 +7040,dirigibile,3,2 +7041,deploy,3,1 +7042,schierare,3,2 +7043,frolic,3,1 +7044,folleggiare,3,2 +7045,insulate,3,1 +7046,isolare,3,2 +7047,luscious,3,1 +7048,succulento,3,2 +7049,ovum,3,1 +7050,ovulo,3,2 +7051,pannier,3,1 +7052,gerla,3,2 +7053,penalty,3,1 +7054,sanzione,3,2 +7055,pushover,3,1 +7056,bersaglio facile,3,2 +7057,spire,3,1 +7058,guglia,3,2 +7059,streak,3,1 +7060,serie,3,2 +7061,supply,3,1 +7062,fornire,3,2 +7063,tenement,3,1 +7064,caseggiato,3,2 +7065,veracity,3,1 +7066,veridicita',3,2 +7067,bawdy,3,1 +7068,sconcio,3,2 +7069,bunk,3,1 +7070,letto a castello,3,2 +7071,buzzkill,3,1 +7072,guastafeste,3,2 +7073,cherry,3,1 +7074,ciliegia,3,2 +7075,closet,3,1 +7076,guardaroba,3,2 +7077,comparison,3,1 +7078,paragone,3,2 +7079,dishwasher,3,1 +7080,lavastoviglie,3,2 +7081,rest,3,1 +7082,riposo,3,2 +7083,ruler,3,1 +7084,righello,3,2 +7085,shell,3,1 +7086,conchiglia,3,2 +7087,tarpaulin,3,1 +7088,incerata,3,2 +7089,tasty,3,1 +7090,gustoso,3,2 +7091,wagon,3,1 +7092,carro,3,2 +7093,wastrel,3,1 +7094,perdigiorno,3,2 +7095,bore,3,1 +7096,noia,3,2 +7097,charred,3,1 +7098,carbonizzato,3,2 +7099,coworker,3,1 +7100,collega,3,2 +7101,disburse,3,1 +7102,sborsare,3,2 +7103,headmaster,3,1 +7104,preside,3,2 +7105,liquorice,3,1 +7106,liquirizia,3,2 +7107,nepotism,3,1 +7108,nepotismo,3,2 +7109,plenty,3,1 +7110,abbondanza,3,2 +7111,rejoin,3,1 +7112,riunire,3,2 +7113,schedule,3,1 +7114,programma,3,2 +7115,sect,3,1 +7116,setta,3,2 +7117,slush,3,1 +7118,fanghiglia,3,2 +7119,treat,3,1 +7120,trattare,3,2 +7121,unique,3,1 +7122,unico,3,2 +7123,abroad,3,1 +7124,all'estero,3,2 +7125,cart,3,1 +7126,carrello,3,2 +7127,crooked,3,1 +7128,storto,3,2 +7129,dairy,3,1 +7130,latticini,3,2 +7131,disinter,3,1 +7132,esumare,3,2 +7133,fastidious,3,1 +7134,meticoloso,3,2 +7135,hovel,3,1 +7136,tugurio,3,2 +7137,item,3,1 +7138,articolo,3,2 +7139,mate,3,1 +7140,partner,3,2 +7141,pick,3,1 +7142,scegliere,3,2 +7143,pungency,3,1 +7144,sapidita',3,2 +7145,purchase,3,1 +7146,acquisto,3,2 +7147,reticent,3,1 +7148,reticente,3,2 +7149,spry,3,1 +7150,arzillo,3,2 +7151,apex,3,1 +7152,apice,3,2 +7153,cask,3,1 +7154,botte,3,2 +7155,core,3,1 +7156,nucleo,3,2 +7157,keg,3,1 +7158,fusto,3,2 +7159,landmark,3,1 +7160,punto di riferimento,3,2 +7161,pallet,3,1 +7162,bancale,3,2 +7163,perjure,3,1 +7164,spergiurare,3,2 +7165,retail,3,1 +7166,al dettaglio,3,2 +7167,rob,3,1 +7168,rapinare,3,2 +7169,scare,3,1 +7170,spavento,3,2 +7171,secede,3,1 +7172,secedere,3,2 +7173,snifter,3,1 +7174,goccetto,3,2 +7175,tank,3,1 +7176,carro armato,3,2 +7177,watermelon,3,1 +7178,anguria,3,2 +7179,ambush,3,1 +7180,imboscata,3,2 +7181,downwind,3,1 +7182,sottovento,3,2 +7183,draught,3,1 +7184,spiffero,3,2 +7185,fin,3,1 +7186,pinna,3,2 +7187,hefty,3,1 +7188,pesante,3,2 +7189,itemize,3,1 +7190,elencare,3,2 +7191,juniper,3,1 +7192,ginepro,3,2 +7193,murder,3,1 +7194,omicidio,3,2 +7195,myth,3,1 +7196,mito,3,2 +7197,quay,3,1 +7198,banchina,3,2 +7199,reindeer,3,1 +7200,renna,3,2 +7201,sphere,3,1 +7202,sfera,3,2 +7203,upwind,3,1 +7204,controvento,3,2 +7205,wild berry,3,1 +7206,frutti di bosco,3,2 +7207,banner,3,1 +7208,striscione,3,2 +7209,coconut,3,1 +7210,noce di cocco,3,2 +7211,duress,3,1 +7212,costrizione,3,2 +7213,frizzy,3,1 +7214,crespo,3,2 +7215,hauteur,3,1 +7216,alterigia,3,2 +7217,inhabit,3,1 +7218,abitare,3,2 +7219,intruder,3,1 +7220,intruso,3,2 +7221,prepackaged,3,1 +7222,preconfezionato,3,2 +7223,rent,3,1 +7224,affitto,3,2 +7225,shield,3,1 +7226,scudo,3,2 +7227,smoulder,3,1 +7228,covare rabbia,3,2 +7229,undo,3,1 +7230,disfare,3,2 +7231,uninhabited,3,1 +7232,disabitato,3,2 +7233,wet,3,1 +7234,bagnato,3,2 +7235,apocryphal,3,1 +7236,apocrifo,3,2 +7237,bangle,3,1 +7238,bracciale,3,2 +7239,creepy,3,1 +7240,raccapricciante,3,2 +7241,dissemble,3,1 +7242,dissimulare,3,2 +7243,facilities,3,1 +7244,servizi,3,2 +7245,gravestone,3,1 +7246,lapide,3,2 +7247,hospice,3,1 +7248,ospizio,3,2 +7249,lassitude,3,1 +7250,fiacchezza,3,2 +7251,lone,3,1 +7252,solitario,3,2 +7253,rush,3,1 +7254,di corsa,3,2 +7255,sideline,3,1 +7256,bordocampo,3,2 +7257,soak,3,1 +7258,immergere,3,2 +7259,trick,3,1 +7260,trucco,3,2 +7261,tryst,3,1 +7262,tresca,3,2 +7263,boil,3,1 +7264,bollire,3,2 +7265,din,3,1 +7266,frastuono,3,2 +7267,downtown,3,1 +7268,centro citta',3,2 +7269,employer,3,1 +7270,datore di lavoro,3,2 +7271,flounder,3,1 +7272,annaspare,3,2 +7273,flow,3,1 +7274,flusso,3,2 +7275,foremost,3,1 +7276,principale,3,2 +7277,icicle,3,1 +7278,stalattite,3,2 +7279,parson,3,1 +7280,parroco,3,2 +7281,spades,3,1 +7282,picche,3,2 +7283,studded,3,1 +7284,costellato,3,2 +7285,truculent,3,1 +7286,truculento,3,2 +7287,undergoing,3,1 +7288,in corso,3,2 +7289,wherein,3,1 +7290,in cui,3,2 +7291,farce,3,1 +7292,farsa,3,2 +7293,fervour,3,1 +7294,fervore,3,2 +7295,girder,3,1 +7296,putrella,3,2 +7297,howl,3,1 +7298,ululare,3,2 +7299,in thrall to,3,1 +7300,alla merce' di,3,2 +7301,intervene,3,1 +7302,intervenire,3,2 +7303,pilgrim,3,1 +7304,pellegrino,3,2 +7305,resonance,3,1 +7306,risonanza,3,2 +7307,seem,3,1 +7308,sembrare,3,2 +7309,surety,3,1 +7310,fideiussore,3,2 +7311,time lapse,3,1 +7312,lasso di tempo,3,2 +7313,trainee,3,1 +7314,tirocinante,3,2 +7315,trough,3,1 +7316,trogolo,3,2 +7317,unerring,3,1 +7318,preciso,3,2 +7319,alloy,3,1 +7320,lega,3,2 +7321,assert,3,1 +7322,asserire,3,2 +7323,awestruck,3,1 +7324,sbalordito,3,2 +7325,baleful,3,1 +7326,malevolo,3,2 +7327,dip,3,1 +7328,intingere,3,2 +7329,drag on,3,1 +7330,andare per le lunghe,3,2 +7331,fired,3,1 +7332,licenziato,3,2 +7333,harpoon,3,1 +7334,arpione,3,2 +7335,mundane,3,1 +7336,mondano,3,2 +7337,polymath,3,1 +7338,eclettico,3,2 +7339,shatter,3,1 +7340,frantumare,3,2 +7341,shift,3,1 +7342,turno,3,2 +7343,spawn,3,1 +7344,dare origine a,3,2 +7345,vent,3,1 +7346,sfiato,3,2 +7347,altogether,3,1 +7348,del tutto,3,2 +7349,annoy,3,1 +7350,scocciare,3,2 +7351,blink,3,1 +7352,lampeggiare,3,2 +7353,croak,3,1 +7354,gracidare,3,2 +7355,forewarn,3,1 +7356,preavvertire,3,2 +7357,intermingle,3,1 +7358,legarsi,3,2 +7359,outcrop,3,1 +7360,affioramento,3,2 +7361,outlast,3,1 +7362,durare piu' a lungo,3,2 +7363,ponytail,3,1 +7364,coda di cavallo,3,2 +7365,portent,3,1 +7366,premonizione,3,2 +7367,puzzlement,3,1 +7368,perplessita',3,2 +7369,redolent,3,1 +7370,rievocativo,3,2 +7371,reef,3,1 +7372,barriera corallina,3,2 +7373,wreckage,3,1 +7374,relitto,3,2 +7375,demijohn,3,1 +7376,damigiana,3,2 +7377,disloyal,3,1 +7378,infedele,3,2 +7379,disrepair,3,1 +7380,pessimo stato,3,2 +7381,earthquake,3,1 +7382,terremoto,3,2 +7383,foal,3,1 +7384,puledro,3,2 +7385,forecast,3,1 +7386,previsione,3,2 +7387,guile,3,1 +7388,scaltrezza,3,2 +7389,indolent,3,1 +7390,indolente,3,2 +7391,obviously,3,1 +7392,senza dubbio,3,2 +7393,prescient,3,1 +7394,preveggente,3,2 +7395,rouse,3,1 +7396,risvegliare,3,2 +7397,scaly,3,1 +7398,squamoso,3,2 +7399,suave,3,1 +7400,soave,3,2 +7401,underline,3,1 +7402,sottolineare,3,2 +7403,ablution,3,1 +7404,abluzione,3,2 +7405,benighted,3,1 +7406,arretrato,3,2 +7407,bordering,3,1 +7408,confinante,3,2 +7409,cottage,3,1 +7410,casolare,3,2 +7411,fob,3,1 +7412,portachiavi,3,2 +7413,garrulous,3,1 +7414,garrulo,3,2 +7415,languid,3,1 +7416,languido,3,2 +7417,musk,3,1 +7418,muschio,3,2 +7419,peremptory,3,1 +7420,perentorio,3,2 +7421,reconnoiter,3,1 +7422,recognizione,3,2 +7423,reprisal,3,1 +7424,rappresaglia,3,2 +7425,secrete,3,1 +7426,secernere,3,2 +7427,stud,3,1 +7428,stallone,3,2 +7429,thrice,3,1 +7430,tre volte,3,2 +7431,catwalk,3,1 +7432,passerella,3,2 +7433,compliance,3,1 +7434,conformita',3,2 +7435,desist,3,1 +7436,desistere,3,2 +7437,gravitas,3,1 +7438,solennita',3,2 +7439,grok,3,1 +7440,groccare,3,2 +7441,headcount,3,1 +7442,conta dei presenti,3,2 +7443,heads up,3,1 +7444,dritta,3,2 +7445,midrange,3,1 +7446,di fascia media,3,2 +7447,nonsense,3,1 +7448,assurdita',3,2 +7449,notch,3,1 +7450,tacca,3,2 +7451,predate,3,1 +7452,precedere,3,2 +7453,ring,3,1 +7454,anello,3,2 +7455,scrounge,3,1 +7456,scroccare,3,2 +7457,waiver,3,1 +7458,rinuncia,3,2 +7459,air quotes,3,1 +7460,virgolette,3,2 +7461,baloney,3,1 +7462,fesseria,3,2 +7463,baton,3,1 +7464,bacchetta,3,2 +7465,deputy,3,1 +7466,deputato,3,2 +7467,expeditious,3,1 +7468,spedito,3,2 +7469,handoff,3,1 +7470,passaggio di consegne,3,2 +7471,harrowing,3,1 +7472,straziante,3,2 +7473,hick,3,1 +7474,campagnolo,3,2 +7475,inescapable,3,1 +7476,ineludibile,3,2 +7477,leave in the lurch,3,1 +7478,piantare in asso,3,2 +7479,on the brink,3,1 +7480,sul punto di,3,2 +7481,restate,3,1 +7482,ribadire,3,2 +7483,retrofit,3,1 +7484,ammodernare,3,2 +7485,scathing,3,1 +7486,critica feroce,3,2 +7487,above and beyond,3,1 +7488,ben oltre,3,2 +7489,as far as,3,1 +7490,per quanto,3,2 +7491,by means of,3,1 +7492,mediante,3,2 +7493,by the way,3,1 +7494,a proposito,3,2 +7495,crag,3,1 +7496,falesia,3,2 +7497,craggy,3,1 +7498,scosceso,3,2 +7499,in order to,3,1 +7500,al fine di,3,2 +7501,jerky,3,1 +7502,a scatti,3,2 +7503,linchpin,3,1 +7504,fulcro,3,2 +7505,over and above,3,1 +7506,in aggiunta a,3,2 +7507,undue,3,1 +7508,indebito,3,2 +7509,wallop,3,1 +7510,sberla,3,2 +7511,wattle,3,1 +7512,acacia,3,2 +7513,way out,3,1 +7514,via d'uscita,3,2 +7515,accede,3,1 +7516,acconsentire,3,2 +7517,acrid,3,1 +7518,acre,3,2 +7519,canopy,3,1 +7520,tettoia,3,2 +7521,cutoff,3,1 +7522,limite massimo,3,2 +7523,extricate,3,1 +7524,districare,3,2 +7525,lintel,3,1 +7526,architrave,3,2 +7527,maze,3,1 +7528,labirinto,3,2 +7529,melt,3,1 +7530,sciogliere,3,2 +7531,nosy,3,1 +7532,impiccione,3,2 +7533,pewter,3,1 +7534,peltro,3,2 +7535,pleasant,3,1 +7536,piacevole,3,2 +7537,raptor,3,1 +7538,rapace,3,2 +7539,verdant,3,1 +7540,verdeggiante,3,2 +7541,wile,3,1 +7542,sotterfugio,3,2 +7543,alacrity,3,1 +7544,alacrita',3,2 +7545,carpet,3,1 +7546,tappeto,3,2 +7547,checker,3,1 +7548,dama,3,2 +7549,club,3,1 +7550,mazza,3,2 +7551,condescend,3,1 +7552,condiscendere,3,2 +7553,divot,3,1 +7554,zolla,3,2 +7555,fiery,3,1 +7556,ardente,3,2 +7557,fitfully,3,1 +7558,intermittente,3,2 +7559,opulent,3,1 +7560,opulento,3,2 +7561,pennant,3,1 +7562,stendardo,3,2 +7563,pigpen,3,1 +7564,porcile,3,2 +7565,smattering,3,1 +7566,infarinatura,3,2 +7567,tread,3,1 +7568,percorrere,3,2 +7569,woozy,3,1 +7570,frastornato,3,2 +7571,afoul,3,1 +7572,in conflitto con,3,2 +7573,amorphous,3,1 +7574,amorfo,3,2 +7575,auburn,3,1 +7576,ramato,3,2 +7577,docker,3,1 +7578,scaricatore di porto,3,2 +7579,gain,3,1 +7580,guadagno,3,2 +7581,haft,3,1 +7582,manico,3,2 +7583,jerkin,3,1 +7584,farsetto,3,2 +7585,mooring,3,1 +7586,ormeggio,3,2 +7587,mound,3,1 +7588,ammasso,3,2 +7589,plank,3,1 +7590,asse,3,2 +7591,potency,3,1 +7592,efficacia,3,2 +7593,prong,3,1 +7594,rebbio,3,2 +7595,redemption,3,1 +7596,redenzione,3,2 +7597,sycophant,3,1 +7598,sicofante,3,2 +7599,ailing,3,1 +7600,in difficolta',3,2 +7601,barefoot,3,1 +7602,scalzo,3,2 +7603,bivouac,3,1 +7604,bivacco,3,2 +7605,conspicuous,3,1 +7606,cospicuo,3,2 +7607,contraption,3,1 +7608,marchingegno,3,2 +7609,egregious,3,1 +7610,vergognoso,3,2 +7611,outstanding,3,1 +7612,eccezionale,3,2 +7613,promenade,3,1 +7614,passeggiata lungomare,3,2 +7615,redouble,3,1 +7616,intensificare,3,2 +7617,simper,3,1 +7618,sorriso falso,3,2 +7619,spinster,3,1 +7620,zitella,3,2 +7621,string,3,1 +7622,spago,3,2 +7623,untimely,3,1 +7624,prematura,3,2 +7625,wrangler,3,1 +7626,mandriano,3,2 +7627,bulge,3,1 +7628,rigonfiamento,3,2 +7629,complexion,3,1 +7630,carnagione,3,2 +7631,consign,3,1 +7632,spedire,3,2 +7633,crew,3,1 +7634,equipaggio,3,2 +7635,excise,3,1 +7636,accisa,3,2 +7637,hark back,3,1 +7638,richiamare alla memoria,3,2 +7639,haunch,3,1 +7640,cosciotto,3,2 +7641,inkling,3,1 +7642,sospetto,3,2 +7643,pavilion,3,1 +7644,padiglione,3,2 +7645,purpose,3,1 +7646,scopo,3,2 +7647,serration,3,1 +7648,dentellatura,3,2 +7649,shanty,3,1 +7650,baracca,3,2 +7651,splint,3,1 +7652,stecca,3,2 +7653,walkway,3,1 +7654,passaggio,3,2 +7655,attire,3,1 +7656,modo di vestire,3,2 +7657,collarbone,3,1 +7658,clavicola,3,2 +7659,discord,3,1 +7660,discordia,3,2 +7661,gutter,3,1 +7662,grondaia,3,2 +7663,lectern,3,1 +7664,leggio,3,2 +7665,missive,3,1 +7666,missiva,3,2 +7667,neuter,3,1 +7668,neutro,3,2 +7669,pendant,3,1 +7670,pendente,3,2 +7671,rash,3,1 +7672,incauto,3,2 +7673,remark,3,1 +7674,osservazione,3,2 +7675,reprobate,3,1 +7676,reprobo,3,2 +7677,stepladder,3,1 +7678,scala a libretto,3,2 +7679,tannery,3,1 +7680,conceria,3,2 +7681,warmonger,3,1 +7682,guerrafondaio,3,2 +7683,adjunct,3,1 +7684,aggiunto,3,2 +7685,aircraft,3,1 +7686,velivolo,3,2 +7687,breastbone,3,1 +7688,sterno,3,2 +7689,halberd,3,1 +7690,alabarda,3,2 +7691,inquest,3,1 +7692,inchiesta,3,2 +7693,pariah,3,1 +7694,paria,3,2 +7695,parietal,3,1 +7696,parietale,3,2 +7697,prudish,3,1 +7698,pudico,3,2 +7699,spacecraft,3,1 +7700,astronave,3,2 +7701,spindly,3,1 +7702,allampanato,3,2 +7703,spittle,3,1 +7704,saliva,3,2 +7705,steed,3,1 +7706,destriero,3,2 +7707,tie,3,1 +7708,cravatta,3,2 +7709,vestibule,3,1 +7710,vestibolo,3,2 +7711,beleaguered,3,1 +7712,bersagliato,3,2 +7713,bespectacled,3,1 +7714,occhialuto,3,2 +7715,bumper,3,1 +7716,paraurti,3,2 +7717,hoodlum,3,1 +7718,delinquente,3,2 +7719,larch,3,1 +7720,larice,3,2 +7721,miscreant,3,1 +7722,furfante,3,2 +7723,parry,3,1 +7724,parare,3,2 +7725,prude,3,1 +7726,puritano,3,2 +7727,pull,3,1 +7728,tirare,3,2 +7729,push,3,1 +7730,premere,3,2 +7731,requisition,3,1 +7732,requisizione,3,2 +7733,righteous,3,1 +7734,virtuoso,3,2 +7735,rout,3,1 +7736,disfatta,3,2 +7737,sombre,3,1 +7738,fosco,3,2 +7739,agog,3,1 +7740,elettrizzato,3,2 +7741,archenemy,3,1 +7742,arcinemico,3,2 +7743,attrition,3,1 +7744,logoramento,3,2 +7745,bedrock,3,1 +7746,basamento,3,2 +7747,brainwash,3,1 +7748,indottrinare,3,2 +7749,chivalry,3,1 +7750,galanteria,3,2 +7751,downright,3,1 +7752,addirittura,3,2 +7753,fowl,3,1 +7754,pollame,3,2 +7755,friction,3,1 +7756,attrito,3,2 +7757,hound,3,1 +7758,segugio,3,2 +7759,instalment,3,1 +7760,rata,3,2 +7761,phoney,3,1 +7762,fasullo,3,2 +7763,sunscreen,3,1 +7764,crema solare,3,2 +7765,tributary,3,1 +7766,immissario,3,2 +7767,beacon,3,1 +7768,segnale luminoso,3,2 +7769,berm,3,1 +7770,banchina,3,2 +7771,bittersweet,3,1 +7772,agrodolce,3,2 +7773,cashew,3,1 +7774,anacardo,3,2 +7775,damsel,3,1 +7776,donzella,3,2 +7777,evildoer,3,1 +7778,malfattore,3,2 +7779,genial,3,1 +7780,cordiale,3,2 +7781,germane,3,1 +7782,attinente,3,2 +7783,lapdog,3,1 +7784,tirapiedi,3,2 +7785,misconstrue,3,1 +7786,fraintendere,3,2 +7787,recess,3,1 +7788,recesso,3,2 +7789,unquiet,3,1 +7790,inquieto,3,2 +7791,walnut,3,1 +7792,noce,3,2 +7793,willful,3,1 +7794,intenzionale,3,2 +7795,loam,3,1 +7796,terriccio,3,2 +7797,out of kilter,3,1 +7798,sbilanciato,3,2 +7799,peerage,3,1 +7800,nobilta',3,2 +7801,polarized,3,1 +7802,antitetico,3,2 +7803,pretext,3,1 +7804,pretesto,3,2 +7805,proctor,3,1 +7806,sorvegliante,3,2 +7807,relive,3,1 +7808,rivivere,3,2 +7809,sneer,3,1 +7810,ghigno,3,2 +7811,stately,3,1 +7812,signorile,3,2 +7813,zealotry,3,1 +7814,fanatismo,3,2 +7815,niche,3,1 +7816,nicchia,3,2 +7817,byway,3,1 +7818,strada secondaria,3,2 +7819,uncoil,3,1 +7820,srotolare,3,2 +7821,debacle,3,1 +7822,fiasco,3,2 +7823,forebode,3,1 +7824,pronosticare,3,2 +7825,tacky,3,1 +7826,di cattivo gusto,3,2 +7827,haunt,3,1 +7828,infestare,3,2 +7829,endearing,3,1 +7830,fa tenerezza,3,2 +7831,airtight,3,1 +7832,ermetico,3,2 +7833,landfall,3,1 +7834,approdo,3,2 +7835,pike,3,1 +7836,lancia,3,2 +7837,mage,3,1 +7838,mago,3,2 +7839,crossword,3,1 +7840,cruciverba,3,2 +7841,gorse,3,1 +7842,ginestra,3,2 +7843,shy away,3,1 +7844,restare in disparte,3,2 +7845,anathema,3,1 +7846,anatema,3,2 +7847,outlying,3,1 +7848,periferico,3,2 +7849,underground,3,1 +7850,sotterraneo,3,2 +7851,exodus,3,1 +7852,esodo,3,2 +7853,torque,3,1 +7854,torcere,3,2 +7855,toad,3,1 +7856,rospo,3,2 +7857,settle,3,1 +7858,risolvere,3,2 +7859,brake,3,1 +7860,freno,3,2 +7861,sort,3,1 +7862,smistare,3,2 +7863,kind of,3,1 +7864,in un certo senso,3,2 +7865,receipt,3,1 +7866,ricevuta,3,2 +7867,wriggle out,3,1 +7868,sfuggire a,3,2 +7869,detail,3,1 +7870,dettaglio,3,2 +7871,seat,3,1 +7872,posto a sedere,3,2 +7873,take a seat,3,1 +7874,si accomodi,3,2 +7875,toothbrush,3,1 +7876,spazzolino,3,2 +7877,madden,3,1 +7878,esasperare,3,2 +7879,tart,3,1 +7880,crostata,3,2 +7881,auspicious,3,1 +7882,propizio,3,2 +7883,stare,3,1 +7884,fissare,3,2 +7885,take care,3,1 +7886,prendere cura,3,2 +7887,grate,3,1 +7888,grattuggiare,3,2 +7889,flag,3,1 +7890,bandiera,3,2 +7891,shade,3,1 +7892,fare ombra,3,2 +7893,grateful,3,1 +7894,riconoscente,3,2 +7895,ruin,3,1 +7896,rovinare,3,2 +7897,load,3,1 +7898,carico,3,2 +7899,coward,3,1 +7900,codardo,3,2 +7901,versed in,3,1 +7902,esperto di,3,2 +7903,messy,3,1 +7904,disordinato,3,2 +7905,cruel,3,1 +7906,crudele,3,2 +7907,interrupt,3,1 +7908,interrompere,3,2 +7909,eggnog,3,1 +7910,zabaione,3,2 +7911,surround,3,1 +7912,circondare,3,2 +7913,fork,3,1 +7914,bivio,3,2 +7915,handle,3,1 +7916,gestire,3,2 +7917,hilarious,3,1 +7918,spassoso,3,2 +7919,blowout,3,1 +7920,vittoria schiacciante,3,2 +7921,heat,3,1 +7922,calore,3,2 +7923,mindless,3,1 +7924,ripetitivo,3,2 +7925,resonant,3,1 +7926,risonante,3,2 +7927,toothsome,3,1 +7928,appetitoso,3,2 +7929,common thread,3,1 +7930,filo conduttore,3,2 +7931,pollute,3,1 +7932,inquinare,3,2 +7933,scent,3,1 +7934,profumo,3,2 +7935,secondhand,3,1 +7936,di seconda mano,3,2 +7937,shellfish,3,1 +7938,mollusco,3,2 +7939,fine,3,1 +7940,bene,3,2 +7941,quaint,3,1 +7942,pittoresco,3,2 +7943,grip,3,1 +7944,tenere stretto,3,2 +7945,voracious,3,1 +7946,vorace,3,2 +7947,sealed,3,1 +7948,sigillato,3,2 +7949,encourage,3,1 +7950,incoraggiare,3,2 +7951,temper,3,1 +7952,carattere irascibile,3,2 +7953,resolute,3,1 +7954,risoluto,3,2 +7955,reminiscent,3,1 +7956,che ricorda,3,2 +7957,steel,3,1 +7958,acciaio,3,2 +7959,sheet,3,1 +7960,foglio,3,2 +7961,sudden,3,1 +7962,repentino,3,2 +7963,committee,3,1 +7964,comitato,3,2 +7965,foregoing,3,1 +7966,quanto precede,3,2 +7967,remarkable,3,1 +7968,notevole,3,2 +7969,somehow,3,1 +7970,in qualche modo,3,2 +7971,knuckle down,3,1 +7972,darsi da fare,3,2 +7973,knock down,3,1 +7974,abbattere,3,2 +7975,undress,3,1 +7976,spogliare,3,2 +7977,trail,3,1 +7978,sentiero,3,2 +7979,yell,3,1 +7980,urlare,3,2 +7981,belligerent,3,1 +7982,bellicoso,3,2 +7983,realize,3,1 +7984,rendersi conto,3,2 +7985,jog,3,1 +7986,corsetta,3,2 +7987,null and void,3,1 +7988,nullo,3,2 +7989,overcome,3,1 +7990,andare oltre,3,2 +7991,arise,3,1 +7992,sorgere,3,2 +7993,devote,3,1 +7994,dedicarsi a,3,2 +7995,attractive,3,1 +7996,attraente,3,2 +7997,holiday,3,1 +7998,vacanza,3,2 +7999,shame,3,1 +8000,vergogna,3,2 +8001,fold,3,1 +8002,ripiegare,3,2 +8003,admin,3,1 +8004,amministratore,3,2 +8005,wilderness,3,1 +8006,landa selvaggia,3,2 +8007,hole,3,1 +8008,buco,3,2 +8009,remind,3,1 +8010,ricordare,3,2 +8011,plug,3,1 +8012,spina elettrica,3,2 +8013,lock,3,1 +8014,bloccare,3,2 +8015,dispensable,3,1 +8016,non essenziale,3,2 +8017,juggle,3,1 +8018,destreggiare,3,2 +8019,nondescript,3,1 +8020,indistinto,3,2 +8021,shut,3,1 +8022,chiudere,3,2 +8023,doll,3,1 +8024,bambola,3,2 +8025,rescue,3,1 +8026,soccorso,3,2 +8027,edible,3,1 +8028,commestibile,3,2 +8029,blow,3,1 +8030,soffiare,3,2 +8031,seed,3,1 +8032,seme,3,2 +8033,worm,3,1 +8034,verme,3,2 +8035,overrate,3,1 +8036,sopravvalutare,3,2 +8037,stitch,3,1 +8038,sutura,3,2 +8039,thoughtless,3,1 +8040,privo di tatto,3,2 +8041,crown,3,1 +8042,corona,3,2 +8043,profuse,3,1 +8044,profuso,3,2 +8045,royal,3,1 +8046,regale,3,2 +8047,idiotic,3,1 +8048,demenziale,3,2 +8049,highfalutin,3,1 +8050,ampolloso,3,2 +8051,woeful,3,1 +8052,afflitto,3,2 +8053,lackadaisical,3,1 +8054,apatico,3,2 +8055,gig,3,1 +8056,ingaggio,3,2 +8057,brokerage,3,1 +8058,intermediazione,3,2 +8059,provision,3,1 +8060,fornitura,3,2 +8061,inconceivable,3,1 +8062,inconcepibile,3,2 +8063,lining,3,1 +8064,imbottitura,3,2 +8065,buzzword,3,1 +8066,parola di moda,3,2 +8067,newsstand,3,1 +8068,edicola,3,2 +8069,everlasting,3,1 +8070,eterno,3,2 +8071,flip,3,1 +8072,rivoltare,3,2 +8073,flip flop,3,1 +8074,infradito,3,2 +8075,life long,3,1 +8076,per tutta la vita,3,2 +8077,protractor,3,1 +8078,goniometro,3,2 +8079,lifeguard,3,1 +8080,bagnino,3,2 +8081,fruition,3,1 +8082,compimento,3,2 +8083,proclaim,3,1 +8084,proclamare,3,2 +8085,unworn,3,1 +8086,non indossato,3,2 +8087,prevaricate,3,1 +8088,tergiversare,3,2 +8089,speech,3,1 +8090,discorso,3,2 +8091,garbage,3,1 +8092,immondizia,3,2 +8093,establish,3,1 +8094,istituire,3,2 +8095,dealership,3,1 +8096,concessionaria,3,2 +8097,veal,3,1 +8098,carne di vitello,3,2 +8099,tribulation,3,1 +8100,tribolazione,3,2 +8101,made up,3,1 +8102,inventato,3,2 +8103,tenant,3,1 +8104,inquilino,3,2 +8105,canary,3,1 +8106,canarino,3,2 +8107,truism,3,1 +8108,ovvieta',3,2 +8109,disdain,3,1 +8110,disdegno,3,2 +8111,innkeeper,3,1 +8112,locandiere,3,2 +8113,hatred,3,1 +8114,odio,3,2 +8115,goner,3,1 +8116,spacciato,3,2 +8117,perpetrate,3,1 +8118,commettere,3,2 +8119,vehement,3,1 +8120,veemente,3,2 +8121,trousseau,3,1 +8122,corredo,3,2 +8123,dossier,3,1 +8124,pratica,3,2 +8125,chicory,3,1 +8126,cicoria,3,2 +8127,aimless,3,1 +8128,senza scopo,3,2 +8129,headset,3,1 +8130,cuffie,3,2 +8131,wrest,3,1 +8132,estorcere,3,2 +8133,cardboard,3,1 +8134,cartone,3,2 +8135,restraint,3,1 +8136,ritegno,3,2 +8137,fishbone,3,1 +8138,lisca di pesce,3,2 +8139,deadly,3,1 +8140,letale,3,2 +8141,reliant,3,1 +8142,dipendere da,3,2 +8143,starry eyed,3,1 +8144,sognatore,3,2 +8145,wastepaper,3,1 +8146,carta straccia,3,2 +8147,shorthand,3,1 +8148,abbreviazione,3,2 +8149,turndown,3,1 +8150,rifiuto,3,2 +8151,hamlet,3,1 +8152,frazione,3,2 +8153,act,3,1 +8154,recitare,3,2 +8155,all in all,3,1 +8156,tutto sommato,3,2 +8157,moron,3,1 +8158,imbecille,3,2 +8159,boost,3,1 +8160,promuovere,3,2 +8161,cross eyed,3,1 +8162,strabico,3,2 +8163,shakeup,3,1 +8164,riorganizzazione,3,2 +8165,bridesmaid,3,1 +8166,damigella,3,2 +8167,long time no see,3,1 +8168,da quanto tempo,3,2 +8169,frame,3,1 +8170,telaio,3,2 +8171,wanderlust,3,1 +8172,voglia di viaggiare,3,2 +8173,cliffhanger,3,1 +8174,finale in sospeso,3,2 +8177,razor,3,1 +8178,rasoio,3,2 +8179,rainstorm,3,1 +8180,temporale,3,2 +8181,footprint,3,1 +8182,impronta,3,2 +8183,bliss,3,1 +8184,beatitudine,3,2 +8185,railroad,3,1 +8186,ferrovia,3,2 +8187,racecourse,3,1 +8188,ippodromo,3,2 +8189,ubiquitous,3,1 +8190,onnipresente,3,2 +8191,pheasant,3,1 +8192,fagiano,3,2 +8193,cop,3,1 +8194,poliziotto,3,2 +8195,slam,3,1 +8196,sbattere,3,2 +8197,crackdown,3,1 +8198,giro di vite,3,2 +8199,bookmark,3,1 +8200,segnalibro,3,2 +8201,matchless,3,1 +8202,ineguagliabile,3,2 +8203,refuel,3,1 +8204,fare benzina,3,2 +8205,easygoing,3,1 +8206,accomodante,3,2 +8207,fundraiser,3,1 +8208,raccolta fondi,3,2 +8209,out of date,3,1 +8210,non aggiornato,3,2 +8211,unsheathe,3,1 +8212,sguainare,3,2 +8213,chimp,3,1 +8214,scimpanze',3,2 +8215,award,3,1 +8216,premio,3,2 +8217,undermine,3,1 +8218,minare,3,2 +8219,ineffectual,3,1 +8220,inefficace,3,2 +8221,currency,3,1 +8222,valuta,3,2 +8223,concurrent,3,1 +8224,concomitante,3,2 +8225,reasonable,3,1 +8226,ragionevole,3,2 +8227,trowel,3,1 +8228,spatola,3,2 +8229,bogeyman,3,1 +8230,spauracchio,3,2 +8231,backlit,3,1 +8232,retroilluminato,3,2 +8233,trap,3,1 +8234,trappola,3,2 +8235,jail,3,1 +8236,carcere,3,2 +8237,cutback,3,1 +8238,riduzione,3,2 +8239,broil,3,1 +8240,arrostire,3,2 +8241,task,3,1 +8242,compito,3,2 +8243,checkpoint,3,1 +8244,posto di blocco,3,2 +8245,moonbeam,3,1 +8246,raggio di luna,3,2 +8247,recapitulate,3,1 +8248,riepilogare,3,2 +8249,all hands,3,1 +8250,tutto l'equipaggio,3,2 +8251,all over,3,1 +8252,dappertutto,3,2 +8253,rocket,3,1 +8254,razzo,3,2 +8255,hokum,3,1 +8256,baggianate,3,2 +8257,uphill,3,1 +8258,in salita,3,2 +8259,gunpowder,3,1 +8260,polvere da sparo,3,2 +8261,evergreen,3,1 +8262,sempreverde,3,2 +8263,obliterate,3,1 +8264,annientare,3,2 +8265,transgress,3,1 +8266,trasgredire,3,2 +8267,sop,3,1 +8268,concessione,3,2 +8269,precept,3,1 +8270,precetto,3,2 +8271,quick,3,1 +8272,breve,3,2 +8273,sidekick,3,1 +8274,aiutante,3,2 +8275,fulfilling,3,1 +8276,appagante,3,2 +8277,servicewoman,3,1 +8278,soldatessa,3,2 +8279,unseat,3,1 +8280,scalzare,3,2 +8281,vestige,3,1 +8282,vestigio,3,2 +8283,sunroof,3,1 +8284,cappotta,3,2 +8285,ordain,3,1 +8286,decretare,3,2 +8287,portcullis,3,1 +8288,saracinesca,3,2 +8289,lengthwise,3,1 +8290,longitudinale,3,2 +8291,submissive,3,1 +8292,remissivo,3,2 +8293,loud,3,1 +8294,rumoroso,3,2 +8295,pounding,3,1 +8296,martellante,3,2 +8297,mesh,3,1 +8298,maglia della rete,3,2 +8299,disavow,3,1 +8300,sconfessare,3,2 +8301,debase,3,1 +8302,svalutare,3,2 +8303,tentative,3,1 +8304,preliminare,3,2 +8305,usurer,3,1 +8306,usuraio,3,2 +8307,watermark,3,1 +8308,filigrana,3,2 +8309,watercolor,3,1 +8310,acquerello,3,2 +8311,invariable,3,1 +8312,immutato,3,2 +8313,quit,3,1 +8314,smettere,3,2 +8315,damage,3,1 +8316,danneggiare,3,2 +8317,unbutton,3,1 +8318,sbottonare,3,2 +8319,droplet,3,1 +8320,gocciolina,3,2 +8321,drop,3,1 +8322,far cadere,3,2 +8323,ivory,3,1 +8324,avorio,3,2 +8325,mint,3,1 +8326,menta,3,2 +8327,pave,3,1 +8328,pavimentare,3,2 +8329,overtire,3,1 +8330,sovraffaticare,3,2 +8331,whale,3,1 +8332,balena,3,2 +8333,roster,3,1 +8334,elenco,3,2 +8335,curse,3,1 +8336,maledizione,3,2 +8337,even,3,1 +8338,uniforme,3,2 +8339,foreword,3,1 +8340,prefazione,3,2 +8341,breastplate,3,1 +8342,corazza,3,2 +8343,hyphen,3,1 +8344,trattino,3,2 +8345,semicolon,3,1 +8346,punto e virgola,3,2 +8347,optician,3,1 +8348,ottico,3,2 +8349,perjury,3,1 +8350,falsa testimonianza,3,2 +8351,race,3,1 +8352,gara,3,2 +8353,spank,3,1 +8354,sculacciare,3,2 +8355,spotless,3,1 +8356,senza macchia,3,2 +8357,westbound,3,1 +8358,diretto a ovest,3,2 +8359,minion,3,1 +8360,seguace,3,2 +8361,storehouse,3,1 +8362,magazzino,3,2 +8363,ping,3,1 +8364,segnale,3,2 +8365,halfway,3,1 +8366,a meta' strada,3,2 +8367,indigo,3,1 +8368,indaco,3,2 +8369,livelihood,3,1 +8370,sussistenza,3,2 +8371,despite,3,1 +8372,nonostante,3,2 +8373,attempt,3,1 +8374,tentativo,3,2 +8375,throw,3,1 +8376,lanciare,3,2 +8377,retrieve,3,1 +8378,reperire,3,2 +8379,waterworks,3,1 +8380,acquedotto,3,2 +8381,painkiller,3,1 +8382,antidolorifico,3,2 +8383,frontman,3,1 +8384,rappresentante,3,2 +8385,cover,3,1 +8386,copertina,3,2 +8387,resonate,3,1 +8388,risuonare,3,2 +8389,overstep,3,1 +8390,oltrepassare,3,2 +8391,allot,3,1 +8392,allocare,3,2 +8393,argue,3,1 +8394,dibattere,3,2 +8395,depict,3,1 +8396,raffigurare,3,2 +8397,forester,3,1 +8398,guardia forestale,3,2 +8399,insole,3,1 +8400,soletta,3,2 +8401,grocery,3,1 +8402,alimentari,3,2 +8403,overreact,3,1 +8404,reazione eccessiva,3,2 +8405,kerb,3,1 +8406,cordolo,3,2 +8407,vicarious,3,1 +8408,vicario,3,2 +8409,lick,3,1 +8410,leccare,3,2 +8411,citizen,3,1 +8412,cittadino,3,2 +8413,muffled,3,1 +8414,ovattato,3,2 +8415,stillness,3,1 +8416,quiete,3,2 +8417,regret,3,1 +8418,rimpianto,3,2 +8419,policy,3,1 +8420,norma,3,2 +8421,storefront,3,1 +8422,vetrina,3,2 +8423,out of the way,3,1 +8424,fuori mano,3,2 +8425,hillside,3,1 +8426,pendio,3,2 +8427,backdoor,3,1 +8428,porta sul retro,3,2 +8429,bullet,3,1 +8430,proiettile,3,2 +8431,addict,3,1 +8432,dipendenza,3,2 +8433,recover,3,1 +8434,riprendersi,3,2 +8435,sling,3,1 +8436,imbracatura,3,2 +8437,vouch,3,1 +8438,garantire per,3,2 +8439,nectar,3,1 +8440,nettare,3,2 +8441,cease,3,1 +8442,cessare,3,2 +8443,proofread,3,1 +8444,rileggere,3,2 +8445,baseline,3,1 +8446,linea guida,3,2 +8447,prophecy,3,1 +8448,profezia,3,2 +8449,deplore,3,1 +8450,deplorare,3,2 +8451,makeshift,3,1 +8452,di ripiego,3,2 +8453,squint,3,1 +8454,strizzare gli occhi,3,2 +8455,complain,3,1 +8456,lamentare,3,2 +8457,sprite,3,1 +8458,spiritello,3,2 +8459,blab,3,1 +8460,spifferare,3,2 +8461,mismatch,3,1 +8462,discrepanza,3,2 +8463,eraser,3,1 +8464,gomma da cancellare,3,2 +8465,misfire,3,1 +8466,fare cilecca,3,2 +8467,adhere,3,1 +8468,aderire,3,2 +8469,handmade,3,1 +8470,fatto a mano,3,2 +8471,wand,3,1 +8472,bacchetta,3,2 +8473,eve,3,1 +8474,vigilia,3,2 +8475,chew,3,1 +8476,masticare,3,2 +8477,instil,3,1 +8478,instillare,3,2 +8479,cutlet,3,1 +8480,cotoletta,3,2 +8481,tassel,3,1 +8482,nappa,3,2 +8483,falsehood,3,1 +8484,menzogna,3,2 +8485,atop,3,1 +8486,in cima,3,2 +8487,mouthwash,3,1 +8488,colluttorio,3,2 +8489,phrase,3,1 +8490,frase,3,2 +8491,shelter,3,1 +8492,rifugio,3,2 +8493,meant,3,1 +8494,destinato,3,2 +8495,hosting,3,1 +8496,accoglienza,3,2 +8497,twist,3,1 +8498,torcere,3,2 +8499,board,3,1 +8500,tavola,3,2 +8501,wire,3,1 +8502,fil di ferro,3,2 +8503,wire transfer,3,1 +8504,bonifico,3,2 +8505,bow,3,1 +8506,arco,3,2 +8507,pool,3,1 +8508,piscina,3,2 +8509,checkmate,3,1 +8510,scacco matto,3,2 +8511,turn,3,1 +8512,girare,3,2 +8513,attach,3,1 +8514,allegare,3,2 +8515,append,3,1 +8516,accodare,3,2 +8517,stem,3,1 +8518,stelo,3,2 +8519,develop,3,1 +8520,sviluppare,3,2 +8521,notice,3,1 +8522,preavviso,3,2 +8523,puff pastry,3,1 +8524,pasta sfoglia,3,2 +8525,byproduct,3,1 +8526,sottoprodotto,3,2 +8527,freewill,3,1 +8528,libero arbitrio,3,2 +8529,freewheel,3,1 +8530,a ruota libera,3,2 +8531,plugin,3,1 +8532,componente aggiuntivo,3,2 +8533,custom,3,1 +8534,consuetudine,3,2 +8535,source,3,1 +8536,origine,3,2 +8537,shorten,3,1 +8538,accorciare,3,2 +8539,outdoor,3,1 +8540,all'aperto,3,2 +8541,preference,3,1 +8542,preferenza,3,2 +8543,saw,3,1 +8544,segare,3,2 +8545,bigmouth,3,1 +8546,pettegolo,3,2 +8547,meager,3,1 +8548,esiguo,3,2 +8549,overblown,3,1 +8550,esagerato,3,2 +8551,stumble,3,1 +8552,inciampare,3,2 +8553,around,3,1 +8554,attorno a,3,2 +8555,loop,3,1 +8556,circuito,3,2 +8557,escape,3,1 +8558,sfuggire,3,2 +8559,faker,3,1 +8560,impostore,3,2 +8561,pitch dark,3,1 +8562,buio pesto,3,2 +8563,dark,3,1 +8564,oscuro,3,2 +8565,detract,3,1 +8566,detrarre,3,2 +8567,script,3,1 +8568,sceneggiatura,3,2 +8569,linesman,3,1 +8570,guardalinee,3,2 +8571,inmate,3,1 +8572,detenuto,3,2 +8573,walkthrough,3,1 +8574,passo per passo,3,2 +8575,startup,3,1 +8576,avvio,3,2 +8577,youth,3,1 +8578,giovinezza,3,2 +8579,overpay,3,1 +8580,strapagare,3,2 +8581,stew,3,1 +8582,zuppa,3,2 +8583,abolish,3,1 +8584,abolire,3,2 +8585,interview,3,1 +8586,intervista,3,2 +8587,footer,3,1 +8588,pie' di pagina,3,2 +8589,header,3,1 +8590,intestazione,3,2 +8591,breastfeed,3,1 +8592,allattare,3,2 +8593,lasting,3,1 +8594,duraturo,3,2 +8595,fairness,3,1 +8596,correttezza,3,2 +8597,birthmark,3,1 +8598,voglia,3,2 +8599,in depth,3,1 +8600,in dettaglio,3,2 +8601,incite,3,1 +8602,incitare,3,2 +8603,genre,3,1 +8604,genere,3,2 +8605,maintain,3,1 +8606,mantenere,3,2 +8607,childhood,3,1 +8608,infanzia,3,2 +8609,dishtowel,3,1 +8610,canovaccio,3,2 +8611,ugly,3,1 +8612,brutto,3,2 +8613,distrust,3,1 +8614,sfiducia,3,2 +8615,barbed wire,3,1 +8616,filo spinato,3,2 +8617,fumes,3,1 +8618,esalazioni,3,2 +8619,amnesty,3,1 +8620,amnistia,3,2 +8621,clear head,3,1 +8622,mente lucida,3,2 +8623,bummer,3,1 +8624,disdetta,3,2 +8625,paper clip,3,1 +8626,graffetta,3,2 +8627,vein,3,1 +8628,vena,3,2 +8629,shinbone,3,1 +8630,tibia,3,2 +8631,fibula,3,1 +8632,perone,3,2 +8633,playful,3,1 +8634,giocoso,3,2 +8635,turnover,3,1 +8636,giro di affari,3,2 +8637,concrete,3,1 +8638,cemento,3,2 +8639,paragliding,3,1 +8640,parapendio,3,2 +8641,affirm,3,1 +8642,affermare,3,2 +8643,elector,3,1 +8644,elettore,3,2 +8645,several,3,1 +8646,parecchi,3,2 +8647,bookshelf,3,1 +8648,libreria,3,2 +8649,bookstore,3,1 +8650,negozio di libri,3,2 +8651,at the crack,3,1 +8652,al sorgere,3,2 +8653,fend off,3,1 +8654,scacciare,3,2 +8655,handful,3,1 +8656,manciata,3,2 +8657,maple,3,1 +8658,acero,3,2 +8659,double bed,3,1 +8660,letto matrimoniale,3,2 +8661,clip,3,1 +8662,fermaglio,3,2 +8663,headrest,3,1 +8664,poggiatesta,3,2 +8665,flagrant,3,1 +8666,flagrante,3,2 +8667,prow,3,1 +8668,prua,3,2 +8669,outlive,3,1 +8670,vivere piu' a lungo,3,2 +8671,teaspoon,3,1 +8672,cucchiaino,3,2 +8673,wrap,3,1 +8674,incartare,3,2 +8675,accountable,3,1 +8676,tenuto a rispondere,3,2 +8677,italics,3,1 +8678,corsivo,3,2 +8679,cross out,3,1 +8680,barrare,3,2 +8681,defeatist,3,1 +8682,rinunciatario,3,2 +8683,bourgeois,3,1 +8684,borghese,3,2 +8685,hairdo,3,1 +8686,acconciatura,3,2 +8687,outsize,3,1 +8688,fuori misura,3,2 +8689,speechless,3,1 +8690,senza parole,3,2 +8691,unleash,3,1 +8692,sguinzagliare,3,2 +8693,tacked,3,1 +8694,imbastito,3,2 +8695,tweezer,3,1 +8696,pinzetta,3,2 +8697,guesswork,3,1 +8698,congettura,3,2 +8699,aftertaste,3,1 +8700,retrogusto,3,2 +8701,hairline,3,1 +8702,attaccatura dei capelli,3,2 +8703,braise,3,1 +8704,brasare,3,2 +8705,seaway,3,1 +8706,rotta,3,2 +8707,congeal,3,1 +8708,rapprendersi,3,2 +8709,heron,3,1 +8710,airone,3,2 +8711,split up,3,1 +8712,dividersi,3,2 +8713,acquire,3,1 +8714,acquisire,3,2 +8715,overheat,3,1 +8716,surriscaldarsi,3,2 \ No newline at end of file diff --git a/examples/file_read_stream/pubspec.yaml b/examples/file_read_stream/pubspec.yaml new file mode 100644 index 0000000..b9fa4aa --- /dev/null +++ b/examples/file_read_stream/pubspec.yaml @@ -0,0 +1,20 @@ +name: file_read_stream +description: > + Example of using fpdart to read an compute a large file using Stream. +version: 2.0.0 +homepage: https://www.sandromaglione.com/ +repository: https://github.com/SandroMaglione/fpdart +publish_to: "none" + +environment: + sdk: ">=3.3.0 <4.0.0" + +dependencies: + http: ^1.2.1 + equatable: ^2.0.5 + fpdart: + path: ../../packages/fpdart + +dev_dependencies: + lints: ^2.0.1 + test: ^1.23.1 diff --git a/packages/fpdart/lib/fpdart.dart b/packages/fpdart/lib/fpdart.dart index af6e037..e6cec5e 100644 --- a/packages/fpdart/lib/fpdart.dart +++ b/packages/fpdart/lib/fpdart.dart @@ -1,4 +1,8 @@ export 'src/effect.dart'; export 'src/exit.dart'; +export 'src/extension/curry_extension.dart'; +export 'src/extension/iterable_extension.dart'; +export 'src/extension/list_extension.dart'; +export 'src/extension/map_extension.dart'; export 'src/function.dart'; export 'src/unit.dart'; From 29331c829b0f45af50b32342a39d89cb9b4a192c Mon Sep 17 00:00:00 2001 From: SandroMaglione <lass.maglio@gmail.com> Date: Tue, 9 Apr 2024 09:18:07 +0900 Subject: [PATCH 91/91] `sleep` --- packages/fpdart/lib/src/effect.dart | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/fpdart/lib/src/effect.dart b/packages/fpdart/lib/src/effect.dart index 9490c3b..2d33fd4 100644 --- a/packages/fpdart/lib/src/effect.dart +++ b/packages/fpdart/lib/src/effect.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:fpdart/fpdart.dart'; import './extension/future_or_extension.dart'; -import './extension/iterable_extension.dart'; import 'unit.dart' as fpdart_unit; part 'async_context.dart'; @@ -206,15 +205,10 @@ final class Effect<E, L, R> extends IEffect<E, L, R> { resume.succeed(null); }); - if (resume._deferred.unsafeCompleted) { + return Effect.succeedLazy(() { timer.cancel(); - return resume._deferred.wait<Null>().match( - onFailure: (_) => fpdart_unit.unit, - onSuccess: (_) => fpdart_unit.unit, - ); - } - - return Effect.unit(); + return fpdart_unit.unit; + }); }, );