From f9ece2cd8940c572cd23403368e1a9c34f5ecd41 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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( + (_) 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 { + final L value; + const _EffectThrow(this.value); +} + +typedef DoAdapterEffect = Future Function(Effect); + +DoAdapterEffect _doAdapter(E env) => + (effect) => effect.runEffect(env).then( + (either) => either.getOrElse((l) => throw _EffectThrow(l)), + ); + +typedef DoFunctionEffect = Future Function(DoAdapterEffect _); + +typedef UnsafeRun = FutureOr> Function(E env); + +final class Effect { + final UnsafeRun _unsafeRun; + + const Effect._(this._unsafeRun); + + factory Effect.tryFuture( + FutureOr 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> runEffect(E env) async => _unsafeRun(env); + + Effect flatMap( + Effect 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 extends Effect { + const AsyncEither._(UnsafeRun run) : super._(run); + + factory AsyncEither._fromEffect(Effect effect) => + AsyncEither._(effect.runEffect); + + factory AsyncEither.tryFuture( + FutureOr Function() execute, + L Function(Object error, StackTrace stackTrace) onError, + ) => + AsyncEither._fromEffect( + Effect.tryFuture(execute, onError), + ); +} + +final class SyncEither extends Effect { + const SyncEither._(UnsafeRun run) : super._(run); + + factory SyncEither._fromEffect(Effect effect) => + SyncEither._(effect.runEffect); + + factory SyncEither.trySync( + R Function() execute, + L Function(Object error, StackTrace stackTrace) onError, + ) => + SyncEither._fromEffect( + Effect.tryFuture(execute, onError), + ); +} + +final class Sync extends Effect { + const Sync._(UnsafeRun run) : super._(run); + + factory Sync._fromEffect(Effect effect) => + Sync._(effect.runEffect); + + factory Sync.make( + R Function() execute, + ) => + Sync._fromEffect( + Effect.tryFuture( + execute, + (_, __) => throw Exception( + "Error when building Sync.make", + ), + ), + ); + + factory Sync.value(R value) => Sync._( + (_) => Either.right(value), + ); + + @override + Sync flatMap(covariant Sync Function(R r) f) { + return Sync._fromEffect(super.flatMap(f)); + } +} + +Effect doEffect(DoFunctionEffect f) => + Effect._( + (env) async { + try { + return Either.of(await f(_doAdapter(env))); + } on _EffectThrow 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 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 extends Effect { + const AsyncEither._(UnsafeRun run) : super._(run); + + factory AsyncEither._fromEffect(Effect effect) => + AsyncEither._(effect._runEffect); + + factory AsyncEither.tryFuture( + FutureOr Function() execute, + L Function(Object error, StackTrace stackTrace) onError, + ) => + AsyncEither._fromEffect( + Effect.tryFuture(execute, onError), + ); + + @override + AsyncEither flatMap(AsyncEither 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 { final L value; const _EffectThrow(this.value); } -typedef DoAdapterEffect = Future Function(Effect); +typedef DoAdapterEffect = Future Function(IEffect); DoAdapterEffect _doAdapter(E env) => - (effect) => effect.runEffect(env).then( + (effect) => effect._runEffect(env).then( (either) => either.getOrElse((l) => throw _EffectThrow(l)), ); @@ -20,35 +25,21 @@ typedef DoFunctionEffect = Future Function(DoAdapterEffect _); typedef UnsafeRun = FutureOr> Function(E env); -final class Effect { +abstract interface class IEffect { final UnsafeRun _unsafeRun; + const IEffect._(this._unsafeRun); - const Effect._(this._unsafeRun); + Future> _runEffect(E env) async => _unsafeRun(env); - factory Effect.tryFuture( - FutureOr Function() execute, - L Function(Object error, StackTrace stackTrace) onError, + @mustBeOverridden + IEffect flatMap( + IEffect Function(R r) f, ) => Effect._( - (env) async { - try { - return Either.right(await execute()); - } catch (e, s) { - return Either.left(onError(e, s)); - } - }, - ); - - Future> runEffect(E env) async => _unsafeRun(env); - - Effect flatMap( - Effect 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 { } } -final class AsyncEither extends Effect { - const AsyncEither._(UnsafeRun run) : super._(run); - - factory AsyncEither._fromEffect(Effect effect) => - AsyncEither._(effect.runEffect); +final class Effect extends IEffect { + const Effect._(UnsafeRun run) : super._(run); - factory AsyncEither.tryFuture( + factory Effect.tryFuture( FutureOr Function() execute, L Function(Object error, StackTrace stackTrace) onError, ) => - AsyncEither._fromEffect( - Effect.tryFuture(execute, onError), - ); -} - -final class SyncEither extends Effect { - const SyncEither._(UnsafeRun run) : super._(run); - - factory SyncEither._fromEffect(Effect effect) => - SyncEither._(effect.runEffect); - - factory SyncEither.trySync( - R Function() execute, - L Function(Object error, StackTrace stackTrace) onError, - ) => - SyncEither._fromEffect( - Effect.tryFuture(execute, onError), + Effect._( + (env) async { + try { + return Either.right(await execute()); + } catch (e, s) { + return Either.left(onError(e, s)); + } + }, ); -} -final class Sync extends Effect { - const Sync._(UnsafeRun run) : super._(run); + Future> call(E env) => _runEffect(env); - factory Sync._fromEffect(Effect effect) => - Sync._(effect.runEffect); - - factory Sync.make( - R Function() execute, - ) => - Sync._fromEffect( - Effect.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 flatMap(covariant Sync Function(R r) f) { - return Sync._fromEffect(super.flatMap(f)); + Effect flatMap(covariant Effect 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 extends IEffect { + const Sync._(UnsafeRun run) : super._(run); + + factory Sync._fromEffect(IEffect effect) => + Sync._(effect._runEffect); + + factory Sync.make( + R Function() execute, + ) => + Sync._fromEffect( + Effect.tryFuture( + execute, + (_, __) => throw Exception( + "Error when building Sync.make", + ), + ), + ); + + factory Sync.value(R value) => Sync._( + (_) => Either.right(value), + ); + + @override + Sync flatMap(covariant Sync 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 extends Effect { + const SyncEither._(UnsafeRun run) : super._(run); + + factory SyncEither._fromEffect(Effect effect) => + SyncEither._(effect._runEffect); + + factory SyncEither.trySync( + R Function() execute, + L Function(Object error, StackTrace stackTrace) onError, + ) => + SyncEither._fromEffect( + Effect.tryFuture(execute, onError), + ); + + @override + SyncEither flatMap(SyncEither 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 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 = Future Function(IEffect); DoAdapterEffect _doAdapter(E env) => (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 = Future Function(DoAdapterEffect _); -typedef UnsafeRun = FutureOr> Function(E env); +typedef UnsafeRun = FutureOr> Function(E env); abstract interface class IEffect { final UnsafeRun _unsafeRun; const IEffect._(this._unsafeRun); - Future> _runEffect(E env) async => _unsafeRun(env); + Future> _runEffect(E env) async => _unsafeRun(env); @mustBeOverridden IEffect flatMap( @@ -37,10 +40,10 @@ abstract interface class IEffect { ) => 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 extends IEffect { 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> call(E env) => _runEffect(env); + Future> call(E env) => _runEffect(env); @override String toString() { @@ -84,9 +87,9 @@ Effect doEffect(DoFunctionEffect f) => Effect._( (env) async { try { - return Either.of(await f(_doAdapter(env))); + return Exit.success(await f(_doAdapter(env))); } on _EffectThrow 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 { + const Exit(); + factory Exit.success(R value) => Success(value); + factory Exit.failure(L value) => Failure(value); +} + +class Success extends Exit { + final R value; + const Success(this.value); +} + +class Failure extends Exit { + 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 extends IEffect { + const NEither._(UnsafeRun run) : super._(run); + + @override + NEither flatMap(covariant NEither 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 extends NEither { + final R value; + NRight(this.value) : super._((_) => Exit.success(value)); +} + +// ignore: missing_override_of_must_be_overridden +class NLeft extends NEither { + 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 extends IEffect { ); factory Sync.value(R value) => Sync._( - (_) => Either.right(value), + (_) => Exit.success(value), ); @override @@ -33,9 +33,9 @@ final class Sync extends IEffect { 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 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( (_) 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 extends IEffect { + const Async._(UnsafeRun run) : super._(run); + + factory Async._fromEffect(IEffect effect) => + Async._(effect._runEffect); + + factory Async.make( + R Function() execute, + ) => + Async._fromEffect( + Effect.tryFuture( + execute, + (_, __) => throw Exception( + "Error when building Async.make", + ), + ), + ); + + factory Async.value(R value) => Async._( + (_) => Exit.success(value), + ); + + @override + Async flatMap(covariant Async Function(R r) f) { + return Async._fromEffect(super.flatMap(f)); + } + + Future 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 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 extends Effect { +final class AsyncEither extends IEffect { const AsyncEither._(UnsafeRun run) : super._(run); - factory AsyncEither._fromEffect(Effect effect) => + factory AsyncEither._fromEffect(IEffect effect) => AsyncEither._(effect._runEffect); factory AsyncEither.tryFuture( @@ -15,7 +15,9 @@ final class AsyncEither extends Effect { ); @override - AsyncEither flatMap(AsyncEither Function(R r) f) { + AsyncEither flatMap(covariant AsyncEither Function(R r) f) { return AsyncEither._fromEffect(super.flatMap(f)); } + + Future> 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 extends Effect { +final class SyncEither extends IEffect { const SyncEither._(UnsafeRun run) : super._(run); - factory SyncEither._fromEffect(Effect effect) => + factory SyncEither._fromEffect(IEffect effect) => SyncEither._(effect._runEffect); factory SyncEither.trySync( @@ -15,7 +15,16 @@ final class SyncEither extends Effect { ); @override - SyncEither flatMap(SyncEither Function(R r) f) { + SyncEither flatMap(covariant SyncEither Function(R r) f) { return SyncEither._fromEffect(super.flatMap(f)); } + + Exit 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 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( (_) 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 extends IEffect { - const Async._(UnsafeRun run) : super._(run); - - factory Async._fromEffect(IEffect effect) => - Async._(effect._runEffect); - - factory Async.make( - R Function() execute, - ) => - Async._fromEffect( - Effect.tryFuture( - execute, - (_, __) => throw Exception( - "Error when building Async.make", - ), - ), - ); - - factory Async.value(R value) => Async._( - (_) => Exit.success(value), - ); - - @override - Async flatMap(covariant Async Function(R r) f) { - return Async._fromEffect(super.flatMap(f)); - } - - Future 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 extends IEffect { - const AsyncEither._(UnsafeRun run) : super._(run); - - factory AsyncEither._fromEffect(IEffect effect) => - AsyncEither._(effect._runEffect); - - factory AsyncEither.tryFuture( - FutureOr Function() execute, - L Function(Object error, StackTrace stackTrace) onError, - ) => - AsyncEither._fromEffect( - Effect.tryFuture(execute, onError), - ); - - @override - AsyncEither flatMap(covariant AsyncEither Function(R r) f) { - return AsyncEither._fromEffect(super.flatMap(f)); - } - - Future> 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 { final L value; @@ -57,11 +53,11 @@ abstract interface class IEffect { final class Effect extends IEffect { const Effect._(UnsafeRun run) : super._(run); - factory Effect.tryFuture( + static Effect tryFuture( FutureOr Function() execute, L Function(Object error, StackTrace stackTrace) onError, ) => - Effect._( + Effect._( (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 extends IEffect { - const Sync._(UnsafeRun run) : super._(run); - - factory Sync._fromEffect(IEffect effect) => - Sync._(effect._runEffect); - - factory Sync.make( - R Function() execute, - ) => - Sync._fromEffect( - Effect.tryFuture( - execute, - (_, __) => throw Exception( - "Error when building Sync.make", - ), - ), - ); - - factory Sync.value(R value) => Sync._( - (_) => Exit.success(value), - ); - - @override - Sync flatMap(covariant Sync 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 extends IEffect { - const SyncEither._(UnsafeRun run) : super._(run); - - factory SyncEither._fromEffect(IEffect effect) => - SyncEither._(effect._runEffect); - - factory SyncEither.trySync( - R Function() execute, - L Function(Object error, StackTrace stackTrace) onError, - ) => - SyncEither._fromEffect( - Effect.tryFuture(execute, onError), - ); - - @override - SyncEither flatMap(covariant SyncEither Function(R r) f) { - return SyncEither._fromEffect(super.flatMap(f)); - } - - Exit 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 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( + final doing = doEffect( (_) 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(10).withEnv()); + 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 { typedef DoAdapterEffect = Future Function(IEffect); -DoAdapterEffect _doAdapter(E env) => - (effect) => effect._runEffect(env).then( - (exit) => switch (exit) { - Failure(value: final value) => throw _EffectThrow(value), - Success(value: final value) => value, - }, - ); +DoAdapterEffect _doAdapter(E env) => (effect) => Future.sync( + () => effect._runEffect(env).then( + (exit) => switch (exit) { + Failure(value: final value) => throw _EffectThrow(value), + Success(value: final value) => value, + }, + ), + ); typedef DoFunctionEffect = Future Function(DoAdapterEffect _); @@ -44,6 +45,14 @@ abstract interface class IEffect { ), ); + @mustBeOverridden + IEffect ap( + IEffect f, + ); + + @mustBeOverridden + IEffect map(V Function(R r) f); + @override String toString() { return "Effect(${_unsafeRun.runtimeType})"; @@ -53,11 +62,11 @@ abstract interface class IEffect { final class Effect extends IEffect { const Effect._(UnsafeRun run) : super._(run); - static Effect tryFuture( + static Effect tryFuture( FutureOr Function() execute, L Function(Object error, StackTrace stackTrace) onError, ) => - Effect._( + Effect._( (env) async { try { return Exit.success(await execute()); @@ -67,6 +76,16 @@ final class Effect extends IEffect { }, ); + factory Effect.succeed(R value) => Effect._((_) async => Exit.success(value)); + + Effect withEnv(E Function(V env) f) => Effect._( + (env) => _unsafeRun(f(env)), + ); + + static Effect ask() => Effect._( + (env) async => Exit.success(env), + ); + Future> call(E env) => _runEffect(env); @override @@ -75,9 +94,21 @@ final class Effect extends IEffect { } @override - Effect flatMap(covariant Effect Function(R r) f) { - return Effect._(super.flatMap(f)._unsafeRun); - } + Effect flatMap(covariant Effect Function(R r) f) => + Effect._(super.flatMap(f)._unsafeRun); + + @override + Effect ap( + covariant Effect f, + ) => + f.flatMap( + (f) => flatMap( + (v) => Effect.succeed(f(v)), + ), + ); + + @override + Effect map(V Function(R r) f) => ap(Effect.succeed(f)); } Effect doEffect(DoFunctionEffect 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 { class Success extends Exit { final R value; const Success(this.value); + + @override + String toString() { + return "Exit.Success($value)"; + } } class Failure extends Exit { 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 extends IEffect { +sealed class NEither extends IEffect { const NEither._(UnsafeRun run) : super._(run); @override @@ -10,6 +10,26 @@ sealed class NEither extends IEffect { NRight(value: final value) => f(value), }; } + + @override + NEither ap( + covariant NEither f, + ) => + f.flatMap( + (f) => flatMap( + (v) => NRight(f(v)), + ), + ); + + @override + NEither map(V Function(R r) f) => ap(NRight(f)); + + Effect withEnv() => 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 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(10).withEnv()); - return beforeEnv + mapped + asEither; + final asOption = await _(NSome(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 { 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 extends IEffect { - const NEither._(UnsafeRun run) : super._(run); + const NEither._(UnsafeRun run) : super._(run); @override NEither flatMap(covariant NEither 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 extends IEffect { + const NOption._(UnsafeRun run) : super._(run); + + @override + NOption flatMap(covariant NOption Function(R r) f) { + return switch (this) { + NNone() => NNone(), + NSome(value: final value) => f(value), + }; + } + + @override + NOption ap( + covariant NOption f, + ) => + f.flatMap( + (f) => flatMap( + (v) => NSome(f(v)), + ), + ); + + @override + NOption map(V Function(R r) f) => ap(NSome(f)); + + Effect withEnv(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 extends NOption { + final R value; + NSome(this.value) : super._((_) => Exit.success(value)); +} + +// ignore: missing_override_of_must_be_overridden +class NNone extends NOption { + NNone() : super._((_) => Exit.failure(null)); +} From 0c23b7831208d41f40943166f16189332080516f Mon Sep 17 00:00:00 2001 From: SandroMaglione 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( (_) 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(10).withEnv()); final asOption = await _(NSome(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 { final class Effect extends IEffect { const Effect._(UnsafeRun run) : super._(run); - static Effect tryFuture( + factory Effect.tryCatch( FutureOr Function() execute, L Function(Object error, StackTrace stackTrace) onError, ) => @@ -77,6 +77,9 @@ final class Effect extends IEffect { }, ); + factory Effect.function(FutureOr 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 withEnv(E Function(V env) f) => Effect._( @@ -110,6 +113,13 @@ final class Effect extends IEffect { @override Effect map(V Function(R r) f) => ap(Effect.succeed(f)); + + Effect mapLeft(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 doEffect(DoFunctionEffect 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 extends IEffect { NRight(value: final value) => Exit.success(value), }, ); + + NEither mapLeft(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 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 { Future> _runEffect(E env) async => _unsafeRun(env); + @mustBeOverridden + IEffect andThen(IEffect Function() then) => + flatMap((_) => then()); + @mustBeOverridden IEffect flatMap( IEffect Function(R r) f, @@ -47,9 +51,7 @@ abstract interface class IEffect { ); @mustBeOverridden - IEffect ap( - IEffect f, - ); + IEffect ap(IEffect f); @mustBeOverridden IEffect map(V Function(R r) f); @@ -114,12 +116,27 @@ final class Effect extends IEffect { @override Effect map(V Function(R r) f) => ap(Effect.succeed(f)); + @override + Effect andThen(covariant Effect Function() then) => + Effect._(super.andThen(then)._unsafeRun); + Effect mapLeft(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 orElse( + covariant Effect Function(L l) orElse, + ) => + Effect._( + (env) async => switch ((await _unsafeRun(env))) { + Failure(value: final value) => orElse(value)._unsafeRun(env), + Success(value: final value) => + Effect.succeed(value)._unsafeRun(env), + }, + ); } Effect doEffect(DoFunctionEffect 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 extends IEffect { } // ignore: missing_override_of_must_be_overridden -class NRight extends NEither { +final class NRight extends NEither { final R value; NRight(this.value) : super._((_) => Exit.success(value)); + + @override + NEither andThen(covariant NEither Function() then) => then(); + + @override + NEither orElse(covariant NEither Function(L l) orElse) => + NRight(value); } // ignore: missing_override_of_must_be_overridden -class NLeft extends NEither { +final class NLeft extends NEither { final L value; NLeft(this.value) : super._((_) => Exit.failure(value)); + + @override + NEither andThen(covariant NEither Function() then) => + NLeft(value); + + NEither orElse(covariant NEither 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 extends IEffect { } // ignore: missing_override_of_must_be_overridden -class NSome extends NOption { +final class NSome extends NOption { final R value; NSome(this.value) : super._((_) => Exit.success(value)); + + @override + NOption andThen(covariant NOption Function() then) => then(); } // ignore: missing_override_of_must_be_overridden -class NNone extends NOption { +final class NNone extends NOption { NNone() : super._((_) => Exit.failure(null)); + + @override + NOption andThen(covariant NOption Function() then) => this; } From e0c2dfa54ff3e7a3bcca53375d39464715cce03f Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 { typedef DoAdapterEffect = Future Function(IEffect); DoAdapterEffect _doAdapter(E env) => (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 _doAdapter(E env) => (effect) => Future.sync( typedef DoFunctionEffect = Future Function(DoAdapterEffect _); -typedef UnsafeRun = FutureOr> Function(E env); - abstract interface class IEffect { - final UnsafeRun _unsafeRun; - const IEffect._(this._unsafeRun); + const IEffect(); + Effect get asEffect; +} - Future> _runEffect(E env) async => _unsafeRun(env); +final class Effect extends IEffect { + final FutureOr> Function(E env) _unsafeRun; - @mustBeOverridden - IEffect andThen(IEffect Function() then) => - flatMap((_) => then()); + const Effect._(this._unsafeRun); - @mustBeOverridden - IEffect flatMap( - IEffect 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 get asEffect => this; - @mustBeOverridden - IEffect ap(IEffect f); + Future> _runEffect(E env) async => _unsafeRun(env); - @mustBeOverridden - IEffect map(V Function(R r) f); + Future> call(E env) => _runEffect(env); @override String toString() { return "Effect(${_unsafeRun.runtimeType})"; } -} - -final class Effect extends IEffect { - const Effect._(UnsafeRun run) : super._(run); factory Effect.tryCatch( FutureOr Function() execute, @@ -92,18 +72,16 @@ final class Effect extends IEffect { (env) async => Exit.success(env), ); - Future> call(E env) => _runEffect(env); - - @override - String toString() { - return "Effect(${_unsafeRun.runtimeType})"; - } - - @override Effect flatMap(covariant Effect 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 ap( covariant Effect f, ) => @@ -113,12 +91,10 @@ final class Effect extends IEffect { ), ); - @override Effect map(V Function(R r) f) => ap(Effect.succeed(f)); - @override Effect andThen(covariant Effect Function() then) => - Effect._(super.andThen(then)._unsafeRun); + flatMap((_) => then()); Effect mapLeft(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 extends IEffect { - const NEither._(UnsafeRun run) : super._(run); + const NEither(); - @override NEither flatMap(covariant NEither Function(R r) f) { return switch (this) { NLeft(value: final value) => NLeft(value), @@ -11,7 +10,6 @@ sealed class NEither extends IEffect { }; } - @override NEither ap( covariant NEither f, ) => @@ -21,7 +19,6 @@ sealed class NEither extends IEffect { ), ); - @override NEither map(V Function(R r) f) => ap(NRight(f)); Effect withEnv() => Effect._( @@ -37,25 +34,26 @@ sealed class NEither extends IEffect { }; } -// ignore: missing_override_of_must_be_overridden final class NRight extends NEither { final R value; - NRight(this.value) : super._((_) => Exit.success(value)); + const NRight(this.value); @override + Effect get asEffect => Effect.succeed(value); + NEither andThen(covariant NEither Function() then) => then(); - @override NEither orElse(covariant NEither Function(L l) orElse) => NRight(value); } -// ignore: missing_override_of_must_be_overridden final class NLeft extends NEither { final L value; - NLeft(this.value) : super._((_) => Exit.failure(value)); + const NLeft(this.value); @override + Effect get asEffect => Effect.fail(value); + NEither andThen(covariant NEither 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 extends IEffect { - const NOption._(UnsafeRun run) : super._(run); + const NOption(); - @override NOption flatMap(covariant NOption Function(R r) f) { return switch (this) { NNone() => NNone(), @@ -11,7 +10,6 @@ sealed class NOption extends IEffect { }; } - @override NOption ap( covariant NOption f, ) => @@ -21,7 +19,6 @@ sealed class NOption extends IEffect { ), ); - @override NOption map(V Function(R r) f) => ap(NSome(f)); Effect withEnv(L Function() onNone) => Effect._( @@ -32,19 +29,21 @@ sealed class NOption extends IEffect { ); } -// ignore: missing_override_of_must_be_overridden final class NSome extends NOption { final R value; - NSome(this.value) : super._((_) => Exit.success(value)); + const NSome(this.value); @override + Effect get asEffect => Effect.succeed(value); + NOption andThen(covariant NOption Function() then) => then(); } -// ignore: missing_override_of_must_be_overridden final class NNone extends NOption { - NNone() : super._((_) => Exit.failure(null)); + const NNone(); @override + Effect get asEffect => Effect.fail(null); + NOption andThen(covariant NOption Function() then) => this; } From 5f945c42a737f300fdaa8fede749ceb4ddc599c9 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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( + final doing = Effect.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(10).withEnv()); 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 { final class Effect extends IEffect { final FutureOr> Function(E env) _unsafeRun; - const Effect._(this._unsafeRun); @override Effect get asEffect => this; + @override + String toString() { + return "Effect(${_unsafeRun.runtimeType})"; + } + + /// {@category execution} Future> _runEffect(E env) async => _unsafeRun(env); + /// {@category execution} Future> 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() => throw Exception("Failed runSync Effect"), + Success(value: final value) => value, + }; + } + + /// {@category execution} + Future runFuture(E env) async { + final result = await _unsafeRun(env); + return switch (result) { + Failure() => throw Exception("Failed runFuture Effect"), + Success(value: final value) => value, + }; } + /// {@category constructors} + // ignore: non_constant_identifier_names + factory Effect.gen(DoFunctionEffect f) => Effect._( + (env) async { + try { + return Exit.success(await f(_doAdapter(env))); + } on _EffectThrow catch (e) { + return Exit.failure(e.value); + } + }, + ); + + /// {@category constructors} factory Effect.tryCatch( FutureOr Function() execute, L Function(Object error, StackTrace stackTrace) onError, @@ -59,31 +95,35 @@ final class Effect extends IEffect { }, ); - factory Effect.function(FutureOr Function() f) => - Effect._((_) async => Exit.success(await f())); + /// {@category constructors} + factory Effect.function(FutureOr 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 unit() => Effect._( + (_) async => Exit.success(null), + ); + + /// {@category do_notation} Effect withEnv(E Function(V env) f) => Effect._( (env) => _unsafeRun(f(env)), ); + /// {@category do_notation} static Effect ask() => Effect._( (env) async => Exit.success(env), ); - Effect flatMap(covariant Effect 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 ap( - covariant Effect f, + Effect f, ) => f.flatMap( (f) => flatMap( @@ -91,20 +131,60 @@ final class Effect extends IEffect { ), ); + /// {@category mapping} Effect map(V Function(R r) f) => ap(Effect.succeed(f)); - Effect andThen(covariant Effect Function() then) => - flatMap((_) => then()); - - Effect mapLeft(C Function(L l) f) => Effect._( + /// {@category mapping} + Effect mapError(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 mapBoth(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 flatMap(Effect 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 tap(Effect Function(R r) f) => + flatMap((r) => f(r).map((_) => r)); + + /// {@category sequencing} + Effect tapError(Effect Function(L l) f) => Effect._( + (env) async { + switch ((await _runEffect(env))) { + case Failure(value: final value): + await f(value)._unsafeRun(env); + return Exit.failure(value); + case Success(value: final value): + return Exit.success(value); + } + }, + ); + + /// {@category sequencing} + Effect andThen(Effect Function() then) => + flatMap((_) => then()); + + /// {@category alternatives} Effect orElse( - covariant Effect Function(L l) orElse, + Effect Function(L l) orElse, ) => Effect._( (env) async => switch ((await _unsafeRun(env))) { @@ -113,15 +193,16 @@ final class Effect extends IEffect { Effect.succeed(value)._unsafeRun(env), }, ); -} -Effect doEffect(DoFunctionEffect f) => - Effect._( - (env) async { - try { - return Exit.success(await f(_doAdapter(env))); - } on _EffectThrow catch (e) { - return Exit.failure(e.value); - } - }, - ); + /// {@category error_handling} + Effect catchAll( + Effect Function(L error) f, + ) => + Effect._( + (env) async => switch ((await _unsafeRun(env))) { + Failure(value: final value) => f(value)._unsafeRun(env), + Success(value: final value) => + Effect.succeed(value)._unsafeRun(env), + }, + ); +} From bddc3b9893c26356eb8f6441147babbf868fa8f4 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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> readAsLines({ + Encoding encoding = utf8, + }) => + Effect.env().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 collectFoundWords( ), ); +typedef Env = ({ + FileSystem fileSystem, + File sourceIta, + File sourceEng, +}); + void main() async { - final collectDoNotation = TaskEither>.Do( + final collectDoNotation = + Effect>.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> 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.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 extends IEffect { ); /// {@category do_notation} - static Effect ask() => Effect._( + static Effect env() => Effect._( (env) async => Exit.success(env), ); @@ -195,14 +195,14 @@ final class Effect extends IEffect { ); /// {@category error_handling} - Effect catchAll( - Effect Function(L error) f, + Effect catchAll( + Effect Function(L error) f, ) => Effect._( (env) async => switch ((await _unsafeRun(env))) { Failure(value: final value) => f(value)._unsafeRun(env), Success(value: final value) => - Effect.succeed(value)._unsafeRun(env), + Effect.succeed(value)._unsafeRun(env), }, ); } From a5af9a8821cb2563a313e35eba7cb5eaacfe5058 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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> readAsLines({ + Encoding encoding = utf8, + }); +} + +final class FileSystemLive extends FileSystem { Effect> 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>( + [], + (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>.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 collectFoundWords( - Iterable<(String, String)> iterable, -) => - iterable.flatMapWithIndex( - (tuple, index) => searchWords.foldLeftWithIndex>( - [], - (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>.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 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> 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 searchWords, Iterable<(String, String)> iterable, ) => iterable.flatMapWithIndex( - (tuple, index) => _searchWords.foldLeftWithIndex>( + (tuple, index) => searchWords.foldLeftWithIndex>( [], (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 searchWords, +}); final program = Effect>.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> 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.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(10).withEnv()); - final asOption = await _(NSome(10).withEnv(() => "Some")); + final mapped = await _(effect.map((r) => r + 10).provide(identity)); + final asEither = await _(NRight(10).provide()); + final asOption = await _(NSome(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 extends IEffect { factory Effect.succeed(R value) => Effect._((_) async => Exit.success(value)); /// {@category constructors} - static Effect unit() => Effect._( + static Effect unit() => Effect._( (_) async => Exit.success(null), ); + /// Extract the required dependency from the complete environment. + /// /// {@category do_notation} - Effect withEnv(E Function(V env) f) => Effect._( + Effect provide(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 extends IEffect { +sealed class NEither extends IEffect { const NEither(); NEither flatMap(covariant NEither Function(R r) f) { @@ -21,7 +21,7 @@ sealed class NEither extends IEffect { NEither map(V Function(R r) f) => ap(NRight(f)); - Effect withEnv() => Effect._( + Effect provide() => 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 extends NEither { const NRight(this.value); @override - Effect get asEffect => Effect.succeed(value); + Effect get asEffect => Effect.succeed(value); NEither andThen(covariant NEither Function() then) => then(); @@ -52,7 +52,7 @@ final class NLeft extends NEither { const NLeft(this.value); @override - Effect get asEffect => Effect.fail(value); + Effect get asEffect => Effect.fail(value); NEither andThen(covariant NEither 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 extends IEffect { +sealed class NOption extends IEffect { const NOption(); NOption flatMap(covariant NOption Function(R r) f) { return switch (this) { - NNone() => NNone(), + NNone() => NNone(), NSome(value: final value) => f(value), }; } @@ -21,7 +21,7 @@ sealed class NOption extends IEffect { NOption map(V Function(R r) f) => ap(NSome(f)); - Effect withEnv(L Function() onNone) => Effect._( + Effect provide(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 extends NOption { const NSome(this.value); @override - Effect get asEffect => Effect.succeed(value); + Effect get asEffect => Effect.succeed(value); NOption andThen(covariant NOption Function() then) => then(); } @@ -43,7 +43,11 @@ final class NNone extends NOption { const NNone(); @override - Effect get asEffect => Effect.fail(null); + @internal + + /// **This will always throw, don't use it!** + // ignore: cast_from_null_always_fails + Effect get asEffect => Effect.fail(null as Never); NOption andThen(covariant NOption Function() then) => this; } From 9d42ff622331846ca2f5b8adf7d654f1f2f1681c Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 extends IEffect { ); /// {@category error_handling} - Effect catchAll( + Effect catchError( Effect Function(L error) f, ) => Effect._( From ae2a3ef4e5c42dd8f10efa8cf18b3e75fa919e14 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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(10).provide()); - final asOption = await _(NSome(10).provide(() => "Some")); + final asEither = await _(Right(10).provide()); + final asOption = await _(Some(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 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 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 { 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 right(R r) => Right(r); - -/// Return a `Left(l)`. -/// -/// Shortcut for `Either.left(l)`. -Either left(L l) => Left(l); - -final class _EitherThrow { - final L value; - const _EitherThrow(this.value); -} - -typedef DoAdapterEither = R Function(Either); -DoAdapterEither _doAdapter() => - (Either either) => either.getOrElse( - (l) => throw _EitherThrow(l), - ); - -typedef DoFunctionEither = R Function(DoAdapterEither $); - -/// 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 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 extends IEffect { const Either(); - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory Either.Do(DoFunctionEither f) { - try { - return Either.of(f(_doAdapter())); - } on _EitherThrow catch (e) { - return Either.left(e.value); - } + Either flatMap(covariant Either 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 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 b, C Function(C acc, R b) f) => - foldMap>(dualEndoMonoid(), (b) => (C c) => f(c, b))(b); - - /// Use `monoid` to combine the value of [Right] applied to `f`. - @override - C foldMap(Monoid 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 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 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 monoid) => foldMap(monoid, identity); - - /// If the [Either] is [Right], then change its value from type `R` to - /// type `C` using function `f`. - @override - Either map(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 bimap(C Function(L l) mLeft, D Function(R b) mRight) => - mapLeft(mLeft).map(mRight); - - /// Return a [Right] containing the value `c`. - @override - Either pure(C c) => Right(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 ap(covariant Either a) => - a.flatMap((f) => map(f)); - - /// If this [Either] is a [Right], then return the result of calling `then`. - /// Otherwise return [Left]. - @override - Either andThen(covariant Either 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 alt(covariant Either 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 map2(covariant Either 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 map3(covariant Either m1, - covariant Either 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` using function `f`. - @override - Either extend(Z Function(Either t) f); - - /// Wrap this [Either] inside another [Either]. - @override - Either> duplicate() => extend(identity); - - /// Chain multiple functions having the same left type `L`. - @override - Either call(covariant Either 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 flatMap(covariant Either Function(R a) f); - - /// {@macro fpdart_flat_map_either} - /// - /// Same as `flatMap`. - Either bind(Either Function(R r) f) => flatMap(f); - - /// If `f` applied on this [Either] as [Right] returns `true`, then return this [Either]. - /// If it returns `false`, return the result of `onFalse` in a [Left]. - Either filterOrElse(bool Function(R r) f, L Function(R r) onFalse) => - flatMap((r) => f(r) ? Either.of(r) : Either.left(onFalse(r))); - - /// Chain a request that returns another [Either], execute it, ignore - /// the result, and return the same value as the current [Either]. - @override - Either chainFirst( - covariant Either Function(R b) chain, - ) => - flatMap((b) => chain(b).map((c) => b).orElse((l) => right(b))); - - /// Used to chain multiple functions that return a `Future`. - /// - /// When this value is [Right], it returns a [TaskEither] that will resolve to - /// the result of calling `f`. - /// Otherwise, if this value is [Left], it returns `TaskEither.left()`. - TaskEither bindFuture(Future> Function(R r) f); - - /// If the [Either] is [Left], then change its value from type `L` to - /// type `C` using function `f`. - Either mapLeft(C Function(L a) f); - - /// 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 toOption(); - - /// Convert this [Either] to a [IOEither]. - IOEither 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 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 getLeft(); - - /// Extract the value from [Right] in a [Option]. - /// - /// If the [Either] is [Left], return [None]. - /// - /// Same as `toOption`. - Option getRight() => toOption(); - - /// Swap the values contained inside the [Left] and [Right] of this [Either]. - Either 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 orElse(Either 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 Function(L l) onLeft, C Function(R r) onRight); - - /// Execute `onLeft` when value is [Left], otherwise execute `onRight`. - /// - /// Same as `match`. - C fold(C Function(L l) onLeft, C Function(R r) onRight) => - match(onLeft, onRight); - - /// Return `true` when value of `r` is equal to the value inside this [Either]. - /// If this [Either] is [Left], then return `false`. - bool elem(R r, Eq eq); - - /// 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>`. - /// - /// 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> traverseListWithIndex( - List list, - Either Function(A a, int i) f, - ) { - final resultList = []; - for (var i = 0; i < list.length; i++) { - final e = f(list[i], i); - if (e is Left) { - return left(e._value); - } else if (e is Right) { - 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> traverseList( - List list, - Either Function(A a) f, - ) => - traverseListWithIndex(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_either} - /// Convert a `List>` to a single `Either>`. - /// - /// If any of the [Either] in the [List] is [Left], then the result is [Left]. - /// {@endtemplate} - static Either> sequenceList( - List> list, + Either ap( + covariant Either f, ) => - traverseList(list, identity); - - /// {@template fpdart_rights_either} - /// Extract all the [Right] values from a `List>`. - /// {@endtemplate} - static List rights(List> list) { - final resultList = []; - for (var i = 0; i < list.length; i++) { - final e = list[i]; - if (e is Right) { - resultList.add(e._value); - } - } - - return resultList; - } - - /// {@template fpdart_lefts_either} - /// Extract all the [Left] values from a `List>`. - /// {@endtemplate} - static List lefts(List> list) { - final resultList = []; - for (var i = 0; i < list.length; i++) { - final e = list[i]; - if (e is Left) { - resultList.add(e._value); - } - } - - return resultList; - } - - /// {@template fpdart_partition_eithers_either} - /// Extract all the [Left] and [Right] values from a `List>` and - /// return them in two partitioned [List] inside a record. - /// {@endtemplate} - static (List, List) partitionEithers(List> list) { - final resultListLefts = []; - final resultListRights = []; - for (var i = 0; i < list.length; i++) { - final e = list[i]; - if (e is Left) { - resultListLefts.add(e._value); - } else if (e is Right) { - 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> 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 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.safeCast` - /// instead of `Either.safeCast`), otherwise this will always return [Right]! - factory Either.safeCast( - dynamic value, - L Function(dynamic value) onError, - ) => - Either.safeCastStrict(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 safeCastStrict( - V value, - L Function(V value) onError, - ) => - value is R ? Either.of(value) : Either.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 Function(T) tryCatchK( - R Function(T) run, L Function(Object o, StackTrace s) onError) => - (a) => Either.tryCatch( - () => run(a), - onError, - ); + Either map(V Function(R r) f) => ap(Right(f)); - /// Build an `Eq` 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> getEq(Eq eqL, Eq 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 provide() => Effect._( + (env) => switch (this) { + Left(value: final value) => Exit.failure(value), + Right(value: final value) => Exit.success(value), + }, + ); - /// Build a `Semigroup` 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> getSemigroup(Semigroup semigroup) => - Semigroup.instance((e1, e2) => e2.match( - (_) => e1, - (r2) => e1.match( - (_) => e2, (r1) => Either.of(semigroup.combine(r1, r2))))); + Either mapLeft(C Function(L l) f) => switch (this) { + Left(value: final value) => Left(f(value)), + Right(value: final value) => Right(value), + }; } -class Right extends Either { - final R _value; - const Right(this._value); - - /// Extract the value of type `R` inside the [Right]. - R get value => _value; +final class Right extends Either { + final R value; + const Right(this.value); @override - Either map2(covariant Either m1, D Function(R b, C c) f) => - flatMap((b) => m1.map((c) => f(b, c))); + Effect get asEffect => Effect.succeed(value); - @override - Either map3(covariant Either m1, - covariant Either m2, E Function(R b, C c, D d) f) => - flatMap((b) => m1.flatMap((c) => m2.map((d) => f(b, c, d)))); + Either andThen(covariant Either Function() then) => then(); - @override - Either map(C Function(R a) f) => Right(f(_value)); - - @override - Either mapLeft(C Function(L a) f) => Right(_value); - - @override - C foldRight(C b, C Function(C acc, R a) f) => f(b, _value); - - @override - C match(C Function(L l) onLeft, C Function(R r) onRight) => - onRight(_value); - - @override - Either flatMap(covariant Either Function(R a) f) => f(_value); - - @override - Option toOption() => Some(_value); - - @override - bool isLeft() => false; - - @override - bool isRight() => true; - - @override - Either swap() => Left(_value); - - @override - Either alt(covariant Either Function() orElse) => this; - - @override - Option getLeft() => Option.none(); - - @override - Either extend(Z Function(Either t) f) => Either.of(f(this)); - - @override - Either orElse(Either Function(L l) onLeft) => - Either.of(_value); - - @override - R getOrElse(R Function(L l) orElse) => _value; - - @override - bool elem(R r, Eq 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 bindFuture(Future> Function(R r) f) => - TaskEither(() async => f(_value)); - - @override - TaskEither toTaskEither() => TaskEither.of(_value); - - @override - IOEither toIOEither() => IOEither.of(_value); - - @override - R? toNullable() => _value; + Either orElse(covariant Either Function(L l) orElse) => + Right(value); } -class Left extends Either { - final L _value; - const Left(this._value); - - /// Extract the value of type `L` inside the [Left]. - L get value => _value; - - @override - Either map2(covariant Either m1, D Function(R b, C c) f) => - flatMap((b) => m1.map((c) => f(b, c))); - - @override - Either map3(covariant Either m1, - covariant Either m2, E Function(R b, C c, D d) f) => - flatMap((b) => m1.flatMap((c) => m2.map((d) => f(b, c, d)))); - - @override - Either map(C Function(R a) f) => Left(_value); - - @override - Either mapLeft(C Function(L a) f) => Left(f(_value)); - - @override - C foldRight(C b, C Function(C acc, R a) f) => b; - - @override - C match(C Function(L l) onLeft, C Function(R r) onRight) => onLeft(_value); - - @override - Either flatMap(covariant Either Function(R a) f) => - Left(_value); - - @override - Option toOption() => Option.none(); - - @override - bool isLeft() => true; - - @override - bool isRight() => false; - - @override - Either swap() => Right(_value); - - @override - Either alt(covariant Either Function() orElse) => orElse(); - - @override - Option getLeft() => Some(_value); - - @override - Either extend(Z Function(Either t) f) => Either.left(_value); - - @override - Either orElse(Either Function(L l) onLeft) => - onLeft(_value); - - @override - R getOrElse(R Function(L l) orElse) => orElse(_value); - - @override - bool elem(R r, Eq 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 bindFuture(Future> Function(R r) f) => - TaskEither.left(_value); +final class Left extends Either { + final L value; + const Left(this.value); @override - TaskEither toTaskEither() => TaskEither.left(_value); + Effect get asEffect => Effect.fail(value); - @override - IOEither toIOEither() => IOEither.left(_value); + Either andThen(covariant Either Function() then) => + Left(value); - @override - R? toNullable() => null; + Either orElse(covariant Either 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 on Iterable { /// Same as `firstOption`. Option 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 on Iterable { /// /// **Note**: Because accessing the last element of an [Iterable] requires /// stepping through all the other elements, `lastOption` **can be slow**. - Option get lastOption => isEmpty ? const None() : some(last); + Option 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 on Iterable { /// 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> get tail => isEmpty ? const None() : some(skip(1)); + Option> 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 on Iterable { /// as if this iterable has only one element. Option> 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 on Iterable { (Iterable, Iterable) 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, Iterable) 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, Iterable) 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, Iterable) splitAt(int n) => (take(n), skip(n)); @@ -184,16 +179,6 @@ extension FpdartOnIterable on Iterable { /// 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 lookupEq(Eq 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 on Iterable { Iterable insertBy(Order 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 on Iterable { 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 on Iterable { 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 on Iterable { 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 on Iterable { /// Return an [Iterable] containing the values of this [Iterable] not included /// in `other` based on `eq`. - Iterable difference(Eq eq, Iterable other) sync* { + Iterable difference(Iterable 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 on Iterable { } /// Sort this [List] based on `order` ([Order]). - List sortBy(Order order) => [...this]..sort(order.compare); + List sortBy(Order 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 sortWith(A Function(T t) extract, Order order) => - [...this]..sort((e1, e2) => order.compare(extract(e1), extract(e2))); + List sortWith(A Function(T t) extract, Order 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 sortWithDate(DateTime Function(T instance) getDate) => - sortWith(getDate, Order.orderDate); + List sortWithDate(DateTime Function(T instance) getDate) => sortWith( + getDate, + Order.comparable(), + ); } /// Functional programming functions on `Iterable>` 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 on List { /// Fold this [List] into a single value by aggregating each element of the list @@ -46,216 +36,3 @@ extension FpdartOnList on List { Iterable dropWhileRight(bool Function(T t) test) => reversed.skipWhile(test); } - -extension FpdartTraversableIterable on Iterable { - /// {@macro fpdart_traverse_list_option} - Option> traverseOptionWithIndex( - Option Function(T a, int i) f, - ) => - Option.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_option} - Option> traverseOption( - Option Function(T a) f, - ) => - Option.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_io_option} - IOOption> traverseIOOptionWithIndex( - IOOption Function(T a, int i) f, - ) => - IOOption.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_io_option} - IOOption> traverseIOOption( - IOOption Function(T a) f, - ) => - IOOption.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_task_option} - TaskOption> traverseTaskOptionWithIndex( - TaskOption Function(T a, int i) f, - ) => - TaskOption.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_task_option} - TaskOption> traverseTaskOption( - TaskOption Function(T a) f, - ) => - TaskOption.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task_option} - TaskOption> traverseTaskOptionWithIndexSeq( - TaskOption Function(T a, int i) f, - ) => - TaskOption.traverseListWithIndexSeq(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task_option} - TaskOption> traverseTaskOptionSeq( - TaskOption Function(T a) f, - ) => - TaskOption.traverseListSeq(toList(), f); - - /// {@macro fpdart_traverse_list_io} - IO> traverseIOWithIndex( - IO Function(T a, int i) f, - ) => - IO.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_io} - IO> traverseIO( - IO Function(T a) f, - ) => - IO.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_task} - Task> traverseTaskWithIndex( - Task Function(T a, int i) f, - ) => - Task.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_task} - Task> traverseTask( - Task Function(T a) f, - ) => - Task.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task} - Task> traverseTaskWithIndexSeq( - Task Function(T a, int i) f, - ) => - Task.traverseListWithIndexSeq(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task} - Task> traverseTaskSeq( - Task Function(T a) f, - ) => - Task.traverseListSeq(toList(), f); - - /// {@macro fpdart_traverse_list_either} - Either> traverseEitherWithIndex( - Either Function(T a, int i) f, - ) => - Either.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_either} - Either> traverseEither( - Either Function(T a) f, - ) => - Either.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_task_either} - TaskEither> traverseTaskEitherWithIndex( - TaskEither Function(T a, int i) f, - ) => - TaskEither.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_task_either} - TaskEither> traverseTaskEither( - TaskEither Function(T a) f, - ) => - TaskEither.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task_either} - TaskEither> traverseTaskEitherWithIndexSeq( - TaskEither Function(T a, int i) f, - ) => - TaskEither.traverseListWithIndexSeq(toList(), f); - - /// {@macro fpdart_traverse_list_seq_task_either} - TaskEither> traverseTaskEitherSeq( - TaskEither Function(T a) f, - ) => - TaskEither.traverseListSeq(toList(), f); - - /// {@macro fpdart_traverse_list_io_either} - IOEither> traverseIOEitherWithIndex( - IOEither Function(T a, int i) f, - ) => - IOEither.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_io_either} - IOEither> traverseIOEither( - IOEither Function(T a) f, - ) => - IOEither.traverseList(toList(), f); - - /// {@macro fpdart_traverse_list_state} - State> traverseStateWithIndex( - State Function(T a, int i) f, - ) => - State.traverseListWithIndex(toList(), f); - - /// {@macro fpdart_traverse_list_state} - State> traverseState( - State Function(T a) f, - ) => - State.traverseList(toList(), f); -} - -extension FpdartSequenceIterableOption on Iterable> { - /// {@macro fpdart_sequence_list_option} - Option> sequenceOption() => Option.sequenceList(toList()); -} - -extension FpdartSequenceIterableIOOption on Iterable> { - /// {@macro fpdart_sequence_list_io_option} - IOOption> sequenceIOOption() => IOOption.sequenceList(toList()); -} - -extension FpdartSequenceIterableTaskOption on Iterable> { - /// {@macro fpdart_sequence_list_task_option} - TaskOption> sequenceTaskOption() => TaskOption.sequenceList(toList()); - - /// {@macro fpdart_sequence_list_seq_task_option} - TaskOption> sequenceTaskOptionSeq() => - TaskOption.sequenceListSeq(toList()); -} - -extension FpdartSequenceIterableIO on Iterable> { - /// {@macro fpdart_sequence_list_io} - IO> sequenceIO() => IO.sequenceList(toList()); -} - -extension FpdartSequenceIterableTask on Iterable> { - /// {@macro fpdart_sequence_list_task} - Task> sequenceTask() => Task.sequenceList(toList()); - - /// {@macro fpdart_sequence_list_seq_task} - Task> sequenceTaskSeq() => Task.sequenceListSeq(toList()); -} - -extension FpdartSequenceIterableEither on Iterable> { - /// {@macro fpdart_sequence_list_either} - Either> sequenceEither() => Either.sequenceList(toList()); - - /// {@macro fpdart_rights_either} - List rightsEither() => Either.rights(toList()); - - /// {@macro fpdart_lefts_either} - List leftsEither() => Either.lefts(toList()); - - /// {@macro fpdart_partition_eithers_either} - (List, List) partitionEithersEither() => - Either.partitionEithers(toList()); -} - -extension FpdartSequenceIterableTaskEither on Iterable> { - /// {@macro fpdart_sequence_list_task_either} - TaskEither> sequenceTaskEither() => - TaskEither.sequenceList(toList()); - - /// {@macro fpdart_sequence_list_seq_task_either} - TaskEither> sequenceTaskEitherSeq() => - TaskEither.sequenceListSeq(toList()); -} - -extension FpdartSequenceIterableIOEither on Iterable> { - /// {@macro fpdart_sequence_list_io_either} - IOEither> sequenceIOEither() => IOEither.sequenceList(toList()); -} - -/// {@macro fpdart_sequence_list_state} -extension FpdartSequenceIterableState on Iterable> { - State> 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 on Map { @@ -55,35 +53,16 @@ extension FpdartOnMap on Map { /// Get the value at given `key` if present, otherwise return [None]. Option 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 lookupKeyEq(Eq 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 lookupEq(Eq 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 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 on Map { /// ``` Option extract(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 on Map { /// ``` Option> extractMap(K key) => extract>(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> modifyAt( - Eq 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 modifyAtIfPresent( - Eq 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> updateAt(Eq 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 updateAtIfPresent( - Eq 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 deleteAt(Eq eq, K key) => - filterWithKey((k, v) => !eq.eqv(k, key)); - - /// Insert or replace a key/value pair in a [Map]. - Map upsertAt(Eq 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)> pop(Eq eq, K key) => lookupEq(eq, key).map( - (v) => (v, deleteAt(eq, key)), - ); + Map 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 on Map { return result; } - /// Combine the key/value of this [Map] and `map` using `combine` where the key is the same. - Map union( - Eq eq, - V Function(V x, V y) combine, - Map 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 intersection( - Eq eq, - V Function(V x, V y) combine, - Map 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 difference(Eq eq, Map map) => filterWithKey( + Map difference(Map 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 eqK, - Eq eqV, - Map map, - ) => - foldLeftWithKey( - 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 on Option { - /// 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 alt(Option Function() orElse) => - this is Some ? this : orElse(); - - /// Return `true` when value of `a` is equal to the value inside the [Option]. - bool elem(T t, Eq 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

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((a) => a.length); - /// ``` - bool Function(A) contramap(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 get toNumOption => Option.fromNullable(num.tryParse(this)); - - /// {@macro fpdart_string_extension_to_int_option} - Option get toIntOption => Option.fromNullable(int.tryParse(this)); - - /// {@macro fpdart_string_extension_to_double_option} - Option get toDoubleOption => - Option.fromNullable(double.tryParse(this)); - - /// {@macro fpdart_string_extension_to_bool_option} - Option get toBoolOption => Option.fromNullable(bool.tryParse(this)); - - /// {@macro fpdart_string_extension_to_num_either} - Either toNumEither(L Function() onLeft) => - Either.fromNullable(num.tryParse(this), onLeft); - - /// {@macro fpdart_string_extension_to_int_either} - Either toIntEither(L Function() onLeft) => - Either.fromNullable(int.tryParse(this), onLeft); - - /// {@macro fpdart_string_extension_to_double_either} - Either toDoubleEither(L Function() onLeft) => - Either.fromNullable(double.tryParse(this), onLeft); - - /// {@macro fpdart_string_extension_to_bool_either} - Either toBoolEither(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 identityFuture(T a) => Future.value(a); /// print(c(112.12)); // -> 10 /// ``` A Function(dynamic b) constF(A a) => (B b) => a; - -/// {@template fpdart_string_extension_to_num_option} -/// Convert this [String] to [num], returns [None] for invalid inputs. -/// {@endtemplate} -Option toNumOption(String str) => str.toNumOption; - -/// {@template fpdart_string_extension_to_int_option} -/// Convert this [String] to [int], returns [None] for invalid inputs. -/// {@endtemplate} -Option toIntOption(String str) => str.toIntOption; - -/// {@template fpdart_string_extension_to_double_option} -/// Convert this [String] to [double], returns [None] for invalid inputs. -/// {@endtemplate} -Option toDoubleOption(String str) => str.toDoubleOption; - -/// {@template fpdart_string_extension_to_bool_option} -/// Convert this [String] to [bool], returns [None] for invalid inputs. -/// {@endtemplate} -Option 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 Function(String) toNumEither(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 Function(String) toIntEither(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 Function(String) toDoubleEither(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 toBoolEither(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(IO); -A _doAdapter(IO io) => io.run(); - -typedef DoFunctionIO = A Function(DoAdapterIO $); - -/// Tag the [HKT] interface for the actual [Option]. -abstract final class _IOHKT {} - -/// `IO` 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 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 f) => IO(() => f(_doAdapter)); - - /// Flat a [IO] contained inside another [IO] to be a single [IO]. - factory IO.flatten(IO> 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 flatMap(covariant IO 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` ([Task]). - Task flatMapTask(Task Function(A a) f) => f(run()); - - /// Convert this [IO] to a [IOEither]. - IOEither toIOEither() => IOEither(() => Either.of(run())); - - /// Lift this [IO] to a [Task]. - /// - /// Return a `Future` ([Task]) instead of a `R` ([IO]). - Task toTask() => Task(() async => run()); - - /// Convert this [IO] to a [TaskEither]. - TaskEither toTaskEither() => - TaskEither(() async => Either.of(run())); - - /// Convert this [IO] to a [TaskOption]. - TaskOption toTaskOption() => TaskOption(() async => Option.of(run())); - - /// Convert this [IO] to a [IOOption]. - IOOption toIOOption() => IOOption(() => Option.of(run())); - - /// Return an [IO] that returns the value `b`. - @override - IO pure(B b) => IO(() => b); - - /// Change the value of type `A` to a value of type `B` using function `f`. - @override - IO map(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 ap(covariant IO 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 map2(covariant IO 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 map3(covariant IO mc, covariant IO 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 call(covariant IO chain) => flatMap((_) => chain); - - /// Chain the result of `then` to this [IO]. - @override - IO andThen(covariant IO 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>`. - /// {@endtemplate} - /// - /// Same as `IO.traverseList` but passing `index` in the map function. - static IO> traverseListWithIndex( - List list, - IO Function(A a, int i) f, - ) => - IO>(() { - final resultList = []; - 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> traverseList( - List list, - IO Function(A a) f, - ) => - traverseListWithIndex(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_io} - /// Convert a `List>` to a single `IO>`. - /// {@endtemplate} - static IO> sequenceList( - List> 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 { - final L value; - const _IOEitherThrow(this.value); -} - -typedef DoAdapterIOEither = A Function(IOEither); -DoAdapterIOEither _doAdapter() => - (ioEither) => ioEither.run().getOrElse((l) => throw _IOEitherThrow(l)); - -typedef DoFunctionIOEither = A Function(DoAdapterIOEither $); - -/// Tag the [HKT2] interface for the actual [IOEither]. -abstract final class _IOEitherHKT {} - -/// `IOEither` 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 extends HKT2<_IOEitherHKT, L, R> - with - Functor2<_IOEitherHKT, L, R>, - Applicative2<_IOEitherHKT, L, R>, - Monad2<_IOEitherHKT, L, R>, - Alt2<_IOEitherHKT, L, R> { - final Either Function() _run; - - /// Build an instance of [IOEither] from `Either Function()`. - const IOEither(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory IOEither.Do(DoFunctionIOEither f) => IOEither(() { - try { - return Either.of(f(_doAdapter())); - } on _IOEitherThrow 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 flatMap(covariant IOEither 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` ([IOEither]) to - /// a function that returns a `Future>` ([TaskEither]). - TaskEither flatMapTask(TaskEither Function(R r) f) => - TaskEither( - () async => run().match( - (l) => Either.left(l), - (r) => f(r).run(), - ), - ); - - /// Convert this [IOEither] to [TaskEither]. - TaskEither toTaskEither() => TaskEither(() async => run()); - - /// Returns a [IOEither] that returns a `Right(a)`. - @override - IOEither pure(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 map2( - covariant IOEither 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 map3(covariant IOEither m1, - covariant IOEither 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 andThen(covariant IOEither 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 map(C Function(R r) f) => ap(pure(f)); - - /// Change the value in the [Left] of [IOEither]. - IOEither mapLeft(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 bimap(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 ap(covariant IOEither a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - /// Change this [IOEither] from `IOEither` to `IOEither`. - IOEither 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 alt(covariant IOEither Function() orElse) => - IOEither(() => run().match((_) => orElse().run(), right)); - - /// Chain multiple functions having the same left type `L`. - @override - IOEither call(covariant IOEither 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 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 orElse(IOEither Function(L l) orElse) => - IOEither(() => run().match( - (l) => orElse(l).run(), (r) => IOEither.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 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 match(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 chainFirst( - covariant IOEither Function(R b) chain, - ) => - flatMap((b) => chain(b).map((c) => b).orElse((l) => IOEither.right(b))); - - /// Run the IO and return a `Either`. - Either 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> 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 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 io) => IOEither(() => Right(io.run())); - - /// Build a [IOEither] from the result of running `io`. - /// - /// Same of `IOEither.rightIO` - factory IOEither.fromIO(IO 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 option, L Function() onNone) => - IOEither(() => option.match( - () => Left(onNone()), - Right.new, - )); - - /// Build a [IOEither] that returns `either`. - factory IOEither.fromEither(Either 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(() { - try { - return Right(run()); - } catch (error, stack) { - return Left(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>`. - /// {@endtemplate} - /// - /// Same as `IOEither.traverseList` but passing `index` in the map function. - static IOEither> traverseListWithIndex( - List list, - IOEither Function(A a, int i) f, - ) => - IOEither>( - () => Either.sequenceList( - IO - .traverseListWithIndex>( - 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> traverseList( - List list, - IOEither Function(A a) f, - ) => - traverseListWithIndex(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_io_either} - /// Convert a `List>` to a single `IOEither>`. - /// {@endtemplate} - static IOEither> sequenceList( - List> 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(IOOption); -A _doAdapter(IOOption iOOption) => iOOption.run().getOrElse( - () => throw const _IOOptionThrow(), - ); - -typedef DoFunctionIOOption = A Function(DoAdapterIOOption $); - -/// Tag the [HKT] interface for the actual [IOOption]. -abstract final class _IOOptionHKT {} - -/// `IOOption` 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 extends HKT<_IOOptionHKT, R> - with - Functor<_IOOptionHKT, R>, - Applicative<_IOOptionHKT, R>, - Monad<_IOOptionHKT, R>, - Alt<_IOOptionHKT, R> { - final Option Function() _run; - - /// Build a [IOOption] from a function returning a `Option`. - const IOOption(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory IOOption.Do(DoFunctionIOOption 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 flatMap(covariant IOOption Function(R r) f) => - IOOption(() => run().match( - Option.none, - (r) => f(r).run(), - )); - - /// Returns a [IOOption] that returns `Some(c)`. - @override - IOOption pure(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 map2(covariant IOOption 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 map3(covariant IOOption m1, covariant IOOption 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 andThen(covariant IOOption Function() then) => - flatMap((_) => then()); - - /// Chain multiple [IOOption] functions. - @override - IOOption call(covariant IOOption chain) => flatMap((_) => chain); - - /// If running this [IOOption] returns [Some], then change its value from type `R` to - /// type `C` using function `f`. - @override - IOOption map(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 ap(covariant IOOption 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 alt(covariant IOOption 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 orElse(IOOption Function() orElse) => - IOOption(() => run().match( - () => orElse().run(), - (r) => IOOption.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 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 match(A Function() onNone, A Function(R r) onSome) => - IO(() => run().match( - onNone, - onSome, - )); - - /// Run the IO and return a `Option`. - Option 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 toIOEither(L Function() onNone) => - IOEither(() => Either.fromOption(run(), onNone)); - - /// Convert this [IOOption] to [TaskOption]. - TaskOption toTaskOption() => 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 toTaskEither(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) => - 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(() { - 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>`. - /// {@endtemplate} - /// - /// Same as `IOOption.traverseList` but passing `index` in the map function. - static IOOption> traverseListWithIndex( - List list, - IOOption Function(A a, int i) f, - ) => - IOOption>( - () => Option.sequenceList( - IO - .traverseListWithIndex>( - 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> traverseList( - List list, - IOOption Function(A a) f, - ) => - traverseListWithIndex(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_io_option} - /// Convert a `List>` to a single `IOOption>`. - /// {@endtemplate} - static IOOption> sequenceList( - List> list, - ) => - traverseList(list, identity); - - /// Build a [IOOption] from `either` that returns [None] when - /// `either` is [Left], otherwise it returns [Some]. - static IOOption fromEither(Either 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 Function(A a) tryCatchK(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 _value; - - IORef._(this._value); - - /// {@template create_io_ref} - /// Creates a new IORef inside an [IO] monad with a given initial value. - /// {@endtemplate} - static IO> create(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 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 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 modify(Endo update) => read().map(update).flatMap(write); -} - -/// [Eq] instance to compare [IORef]s using pointer equality -final ioRefEq = Eq.instance>((a, b) => identical(a, b)); - -/// {@macro create_io_ref} -IO> newIORef(T initial) => IORef.create(initial); - -/// {@macro read_io_ref} -IO readIORef(IORef ref) => ref.read(); - -/// {@macro write_io_ref} -IO writeIORef(T value, IORef ref) => ref.write(value); - -/// {@template io_ref_curried_version} -/// A curried version of the -/// {@endtemplate} -/// [writeIORef] -IO Function(IORef ref) writeIORefC(T value) => - (ref) => ref.write(value); - -/// {@macro modify_io_ref} -IO modifyIORef(Endo update, IORef ref) => ref.modify(update); - -/// {@macro io_ref_curried_version} -/// [modifyIORef] -IO Function(IORef ref) modifyIORefC(Endo 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 extends IEffect { - const NEither(); - - NEither flatMap(covariant NEither Function(R r) f) { - return switch (this) { - NLeft(value: final value) => NLeft(value), - NRight(value: final value) => f(value), - }; - } - - NEither ap( - covariant NEither f, - ) => - f.flatMap( - (f) => flatMap( - (v) => NRight(f(v)), - ), - ); - - NEither map(V Function(R r) f) => ap(NRight(f)); - - Effect provide() => Effect._( - (env) => switch (this) { - NLeft(value: final value) => Exit.failure(value), - NRight(value: final value) => Exit.success(value), - }, - ); - - NEither mapLeft(C Function(L l) f) => switch (this) { - NLeft(value: final value) => NLeft(f(value)), - NRight(value: final value) => NRight(value), - }; -} - -final class NRight extends NEither { - final R value; - const NRight(this.value); - - @override - Effect get asEffect => Effect.succeed(value); - - NEither andThen(covariant NEither Function() then) => then(); - - NEither orElse(covariant NEither Function(L l) orElse) => - NRight(value); -} - -final class NLeft extends NEither { - final L value; - const NLeft(this.value); - - @override - Effect get asEffect => Effect.fail(value); - - NEither andThen(covariant NEither Function() then) => - NLeft(value); - - NEither orElse(covariant NEither 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 extends IEffect { - const NOption(); - - NOption flatMap(covariant NOption Function(R r) f) { - return switch (this) { - NNone() => NNone(), - NSome(value: final value) => f(value), - }; - } - - NOption ap( - covariant NOption f, - ) => - f.flatMap( - (f) => flatMap( - (v) => NSome(f(v)), - ), - ); - - NOption map(V Function(R r) f) => ap(NSome(f)); - - Effect provide(L Function() onNone) => Effect._( - (env) => switch (this) { - NNone() => Exit.failure(onNone()), - NSome(value: final value) => Exit.success(value), - }, - ); -} - -final class NSome extends NOption { - final R value; - const NSome(this.value); - - @override - Effect get asEffect => Effect.succeed(value); - - NOption andThen(covariant NOption Function() then) => then(); -} - -final class NNone extends NOption { - const NNone(); - - @override - @internal - - /// **This will always throw, don't use it!** - // ignore: cast_from_null_always_fails - Effect get asEffect => Effect.fail(null as Never); - - NOption andThen(covariant NOption 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 some(T t) => Some(t); - -/// Return a [None]. -/// -/// Shortcut for `Option.none()`. -Option none() => const Option.none(); - -/// Return [None] if `t` is `null`, [Some] otherwise. -/// -/// Same as initializing `Option.fromNullable(t)`. -Option optionOf(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 option(T value, bool Function(T) predicate) => - Option.fromPredicate(value, predicate); - -final class _OptionThrow { - const _OptionThrow(); -} - -typedef DoAdapterOption = A Function(Option); -A _doAdapter(Option option) => - option.getOrElse(() => throw const _OptionThrow()); - -typedef DoFunctionOption = A Function(DoAdapterOption $); - -/// Tag the [HKT] interface for the actual [Option]. -abstract final class _OptionHKT {} - -// `Option implements Functor` expresses correctly the -// return type of the `map` function as `HKT`. -// 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 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 extends HKT<_OptionHKT, T> - with - Functor<_OptionHKT, T>, - Applicative<_OptionHKT, T>, - Monad<_OptionHKT, T>, - Extend<_OptionHKT, T>, - Filterable<_OptionHKT, T> { +sealed class Option extends IEffect { const Option(); - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory Option.Do(DoFunctionOption 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 mStr = Option.of('name'); - /// final Option mInt = mStr.map((a) => a.length); - /// ``` - /// 👇 - /// ```dart - /// [🥚].map((🥚) => 👨‍🍳(🥚)) -> [🍳] - /// [_].map((🥚) => 👨‍🍳(🥚)) -> [_] - /// ``` - @override - Option map(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 map = a.map( - /// (a) => (int b) => sumToDouble(a, b), - /// ); - /// - /// /// Using `ap`, we get the final `Option` that we want 🚀 - /// final result = b.ap(map); - /// ``` - @override - Option ap(covariant Option a) => a.match( - () => Option.none(), - (f) => map(f), - ); - - /// Return a [Some] containing the value `b`. - @override - Option pure(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 result = a.flatMap( - /// (s) => stringToInt(s).flatMap( - /// (i) => intToDouble(i), - /// ), - /// ); - /// ``` - /// 👇 - /// ```dart - /// [😀].flatMap( - /// (😀) => [👻(😀)] - /// ) -> [😱] - /// - /// [😀].flatMap( - /// (😀) => [👻(😀)] - /// ).flatMap( - /// (😱) => [👨‍⚕️(😱)] - /// ) -> [🤕] - /// - /// [😀].flatMap( - /// (😀) => [_] - /// ).flatMap( - /// (_) => [👨‍⚕️(_)] - /// ) -> [_] - /// - /// [_].flatMap( - /// (😀) => [👻(😀)] - /// ) -> [_] - /// ``` - @override - Option flatMap(covariant Option 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 flatMapNullable(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 flatMapThrowable(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` using function `f`. - @override - Option extend(Z Function(Option t) f); - - /// Wrap this [Option] inside another [Option]. - @override - Option> duplicate() => extend(identity); - - /// If this [Option] is a [Some] and calling `f` returns `true`, then return this [Some]. - /// Otherwise return [None]. - @override - Option 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 filterMap(Option 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, Option) 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, Option) partitionMap(Either 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 andThen(covariant Option Function() then) => - flatMap((_) => then()); - - /// Chain multiple [Option]s. - @override - Option call(covariant Option 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 map2(covariant Option 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 map3(covariant Option mc, covariant Option 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 Function() onNone, B Function(T t) onSome); - - /// {@macro fpdart_option_match} - /// ```dart - /// [🍌].fold(() => 🍎, (🍌) => 🍌 * 2) -> 🍌🍌 - /// [_].fold(() => 🍎, (🍌) => 🍌 * 2) -> 🍎 - /// ``` - /// - /// Same as `match`. - B fold(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 toEither(L Function() onLeft) => match( - () => Left(onLeft()), - Right.new, - ); - - /// Convert this [Option] to a [IOOption]. - IOOption 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 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>`. - /// - /// 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> traverseListWithIndex( - List list, - Option Function(A a, int i) f, - ) { - final resultList = []; - for (var i = 0; i < list.length; i++) { - final o = f(list[i], i); - final r = o.match(() => null, identity); - if (r == null) return none(); - resultList.add(r); - } - - return some(resultList); + Option flatMap(covariant Option 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> traverseList( - List list, - Option Function(A a) f, - ) => - traverseListWithIndex(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_option} - /// Convert a `List>` to a single `Option>`. - /// - /// If any of the [Option] in the [List] is [None], then the result is [None]. - /// {@endtemplate} - static Option> sequenceList( - List> list, + Option ap( + covariant Option f, ) => - traverseList(list, identity); - - /// Build a [Option] from a [Either] by returning [Some] when `either` is [Right], - /// [None] otherwise. - static Option fromEither(Either 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.safeCast` - /// instead of `Option.safeCast`), otherwise this will always return [Some]! - factory Option.safeCast(dynamic value) => - Option.safeCastStrict(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 safeCastStrict(V value) => - value is T ? Option.of(value) : Option.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( - /// str, - /// (str) => str.isNotEmpty, - /// (str) => str.length, - /// ); - /// ``` - static Option fromPredicateMap( - 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; - - /// Flat a [Option] contained inside another [Option] to be a single [Option]. - factory Option.flatten(Option> 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>`. - /// - /// 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, Option) separate(Option> 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 mapInput(T Function(A) map) => Order( + (a1, a2) => compare(map(a1), map(a2)), + ); + + /// Return an [Order] reversed. + Order 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` to an `Order` using `f` + static Order by(B Function(A a) f, Order 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 whenEqual(Order first, Order 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 allEqual() => 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 get random => IO(() => Random().nextDouble()); - -/// Generates a random boolean value. -/// -/// [IO] wrapper around dart `Random().nextBool()`. -IO get randomBool => IO(() => Random().nextBool()); - -/// Generates a non-negative random integer uniformly distributed -/// in the range from `min`, **inclusive**, to `max`, **exclusive**. -IO 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` allows to read values `A` from a dependency/context `R` -/// without explicitly passing the dependency between multiple nested -/// function calls. -final class Reader extends HKT2 - with - Functor2, - Applicative2, - Monad2 { - 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> 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 ap(covariant Reader a) => - Reader((r) => a.run(r)(run(r))); - - /// Used to chain multiple functions that return a [Reader]. - @override - Reader flatMap(covariant Reader Function(A a) f) => - Reader((r) => f(run(r)).run(r)); - - /// Return a [Reader] containing the value `c`. - @override - Reader pure(C c) => Reader((_) => c); - - /// Change the value of type `A` to a value of type `C` using function `f`. - @override - Reader map(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 map2(covariant Reader 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 map3(covariant Reader m1, - covariant Reader 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 andThen(covariant Reader Function() then) => - flatMap((_) => then()); - - /// Chain multiple functions having the reader `R`. - @override - Reader call(covariant Reader chain) => flatMap((_) => chain); - - /// Compose the dependency `R` of this [Reader] to `reader`. - Reader compose(Reader reader) => Reader((r) => reader.run(r)); - - /// Change dependency type of `Reader` from `R` to `R1` after calling `run`. - Reader local(R Function(R1 context) f) => Reader((r) => run(f(r))); - - /// Read the current dependency `R`. - Reader ask() => Reader(identity); - - /// Change reading function to `f` given context/dependency `R`. - Reader 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 chainFirst( - covariant Reader 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 = Future Function(ReaderTask); -DoAdapterReaderTask _doAdapter(E env) => - (ReaderTask task) => task.run(env); - -typedef DoFunctionReaderTask = Future Function( - DoAdapterReaderTask $); - -/// 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 extends HKT2<_ReaderTaskHKT, E, A> - with - Functor2<_ReaderTaskHKT, E, A>, - Applicative2<_ReaderTaskHKT, E, A>, - Monad2<_ReaderTaskHKT, E, A> { - final Future 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 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> 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 ap(covariant ReaderTask 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 flatMap(covariant ReaderTask Function(A a) f) => - ReaderTask( - (env) => run(env).then( - (v) => f(v).run(env), - ), - ); - - /// Return a [ReaderTask] returning the value `b`. - @override - ReaderTask pure(B a) => ReaderTask((_) async => a); - - /// Change the returning value of the [ReaderTask] from type - /// `A` to type `B` using `f`. - @override - ReaderTask map(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 map2( - covariant ReaderTask 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 map3( - covariant ReaderTask mc, - covariant ReaderTask 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 andThen(covariant ReaderTask Function() then) => - flatMap( - (_) => then(), - ); - - @override - ReaderTask chainFirst( - covariant ReaderTask Function(A a) chain, - ) => - flatMap( - (a) => chain(a).map((b) => a), - ); - - /// Chain multiple [ReaderTask] functions. - @override - ReaderTask call(covariant ReaderTask chain) => flatMap( - (_) => chain, - ); - - /// Run the task and return a [Future]. - Future run(E env) => _run(env); - - /// Convert this [ReaderTask] to [ReaderTaskEither]. - ReaderTaskEither toReaderTaskEither() => 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 ask() => 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 { - final L value; - const _ReaderTaskEitherThrow(this.value); -} - -typedef DoAdapterReaderTaskEither = Future Function( - ReaderTaskEither); - -DoAdapterReaderTaskEither _doAdapter(E env) => - (readerTaskEither) => readerTaskEither.run(env).then( - (either) => either.getOrElse((l) => throw _ReaderTaskEitherThrow(l)), - ); - -typedef DoFunctionReaderTaskEither = Future Function( - DoAdapterReaderTaskEither $); - -/// Tag the [HKT3] interface for the actual [ReaderTaskEither]. -abstract final class _ReaderTaskEitherHKT {} - -/// `ReaderTaskEither` 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 - 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> Function(E env) _run; - - /// Build a [ReaderTaskEither] from a function returning a `Future>`. - const ReaderTaskEither(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory ReaderTaskEither.Do(DoFunctionReaderTaskEither f) => - ReaderTaskEither((env) async { - try { - return Either.of(await f(_doAdapter(env))); - } on _ReaderTaskEitherThrow catch (e) { - return Either.left(e.value); - } - }); - - /// Run the task given `E` and return a `Future>`. - Future> run(E env) => _run(env); - - /// Returns a [ReaderTaskEither] that returns a `Right(a)`. - @override - ReaderTaskEither pure(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 flatMap( - covariant ReaderTaskEither 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 flatMapTaskEither( - TaskEither 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 andThen( - covariant ReaderTaskEither 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 map(C Function(R r) f) => ap(pure(f)); - - @override - ReaderTaskEither map2( - covariant ReaderTaskEither m1, - N2 Function(R p1, N1 p2) f, - ) => - flatMap((b) => m1.map((c) => f(b, c))); - - @override - ReaderTaskEither map3( - covariant ReaderTaskEither m1, - covariant ReaderTaskEither 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 mapLeft(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 bimap( - 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 ap( - covariant ReaderTaskEither a, - ) => - a.flatMap( - (f) => flatMap( - (v) => pure(f(v)), - ), - ); - - @override - ReaderTaskEither chainFirst( - covariant ReaderTaskEither Function(R p1) chain, - ) => - flatMap((b) => chain(b).map((c) => b)); - - /// Chain multiple functions having the same left type `L`. - @override - ReaderTaskEither call( - covariant ReaderTaskEither chain, - ) => - flatMap((_) => chain); - - /// Change this [ReaderTaskEither] from `ReaderTaskEither` to `ReaderTaskEither`. - ReaderTaskEither 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 alt( - covariant ReaderTaskEither 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 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 orElse( - ReaderTaskEither Function(L l) orElse, - ) => - ReaderTaskEither((env) async => (await run(env)).match( - (l) => orElse(l).run(env), - (r) => ReaderTaskEither.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 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 match( - 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> readerTaskEither, - ) => - readerTaskEither.flatMap(identity); - - /// Build a [ReaderTaskEither] that returns a [Right] containing the result of running `reader`. - factory ReaderTaskEither.fromReader(Reader reader) => ReaderTaskEither( - (env) async => Right(reader.run(env)), - ); - - /// Build a [ReaderTaskEither] from a `Reader`. - factory ReaderTaskEither.leftReader(Reader 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 task) => ReaderTaskEither( - (_) => task.run().then(left), - ); - - /// Build a [ReaderTaskEither] that returns a [Right] containing the result of running `task`. - factory ReaderTaskEither.fromTask(Task 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 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 task) => - ReaderTaskEither( - (_) async => task.run(), - ); - - /// Build a [ReaderTaskEither] that returns a [Right] containing the result of running `io`. - factory ReaderTaskEither.fromIO(IO 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 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 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 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 option, L Function() onNone) => - ReaderTaskEither((_) async => option.match( - () => Left(onNone()), - Right.new, - )); - - /// Build a [ReaderTaskEither] that returns `either`. - factory ReaderTaskEither.fromEither(Either 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 Function(E) run, - L Function(Object error, StackTrace stackTrace) onError, - ) => - ReaderTaskEither((env) async { - try { - return Right(await run(env)); - } catch (error, stack) { - return Left(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 ask() => 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` 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 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> state) => state.flatMap(identity); - - /// Lift a sync [State] to an async [StateAsync]. - StateAsync toStateAsync() => StateAsync.fromState(this); - - /// Used to chain multiple functions that return a [State]. - @override - State flatMap(covariant State 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 ap(covariant State a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - /// Return a `State` containing `c` as value. - @override - State pure(C c) => State((state) => (c, state)); - - /// Change the value inside `State` from type `A` to type `C` using `f`. - @override - State map(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 map2(covariant State 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 map3(covariant State m1, covariant State 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 andThen(covariant State Function() then) => - flatMap((_) => then()); - - /// Chain multiple functions having the same state `S`. - @override - State call(covariant State state) => flatMap((_) => state); - - /// Extract the current state `S`. - State get() => State((state) => (state, state)); - - /// Change the value getter based on the current state `S`. - State gets(A Function(S state) f) => - State((state) => (f(state), state)); - - /// Change the current state `S` using `f` and return nothing ([Unit]). - State modify(S Function(S state) f) => - State((state) => (unit, f(state))); - - /// Set a new state and return nothing ([Unit]). - State 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 chainFirst( - covariant State 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>`. - /// {@endtemplate} - /// - /// Same as `State.traverseList` but passing `index` in the map function. - static State> traverseListWithIndex( - List list, State Function(A a, int i) f) { - return State((state) { - final resultList = []; - 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> traverseList( - List list, State Function(A a) f) => - traverseListWithIndex(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_state} - /// Convert a `List>` to a single `State>`. - /// {@endtemplate} - static State> sequenceList(List> 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` 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 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> state) => - state.flatMap(identity); - - /// Build a new [StateAsync] by lifting a sync [State] to async. - factory StateAsync.fromState(State state) => - StateAsync((s) async => state.run(s)); - - /// Used to chain multiple functions that return a [StateAsync]. - @override - StateAsync flatMap(covariant StateAsync 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 ap(covariant StateAsync a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - /// Return a `StateAsync` containing `c` as value. - @override - StateAsync pure(C c) => StateAsync((state) async => (c, state)); - - /// Change the value inside `StateAsync` from type `A` to type `C` using `f`. - @override - StateAsync map(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 map2( - covariant StateAsync 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 map3(covariant StateAsync m1, - covariant StateAsync 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 andThen(covariant StateAsync Function() then) => - flatMap((_) => then()); - - /// Chain multiple functions having the same state `S`. - @override - StateAsync call(covariant StateAsync state) => - flatMap((_) => state); - - /// Extract the current state `S`. - StateAsync get() => StateAsync((state) async => (state, state)); - - /// Change the value getter based on the current state `S`. - StateAsync gets(A Function(S state) f) => - StateAsync((state) async => (f(state), state)); - - /// Change the current state `S` using `f` and return nothing ([Unit]). - StateAsync modify(S Function(S state) f) => - StateAsync((state) async => (unit, f(state))); - - /// Set a new state and return nothing ([Unit]). - StateAsync 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 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 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 chainFirst( - covariant StateAsync 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 Function(Task); -Future _doAdapter(Task task) => task.run(); - -typedef DoFunctionTask = Future 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 extends HKT<_TaskHKT, A> - with Functor<_TaskHKT, A>, Applicative<_TaskHKT, A>, Monad<_TaskHKT, A> { - final Future 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 f) => Task(() => f(_doAdapter)); - - /// Build a [Task] that returns `a`. - factory Task.of(A a) => Task(() async => a); - - /// Flat a [Task] contained inside another [Task] to be a single [Task]. - factory Task.flatten(Task> 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 ap(covariant Task 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 flatMap(covariant Task Function(A a) f) => - Task(() => run().then((v) => f(v).run())); - - /// Return a [Task] returning the value `b`. - @override - Task pure(B a) => Task(() async => a); - - /// Change the returning value of the [Task] from type - /// `A` to type `B` using `f`. - @override - Task map(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 map2(covariant Task 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 map3(covariant Task mc, covariant Task 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 andThen(covariant Task Function() then) => - flatMap((_) => then()); - - @override - Task chainFirst(covariant Task Function(A a) chain) => - flatMap((a) => chain(a).map((b) => a)); - - /// Chain multiple [Task] functions. - @override - Task call(covariant Task chain) => flatMap((_) => chain); - - /// Creates a task that will complete after a time delay specified by a [Duration]. - Task delay(Duration duration) => Task(() => Future.delayed(duration, run)); - - /// Run the task and return a [Future]. - Future run() => _run(); - - /// Convert this [Task] to [TaskOption]. - TaskOption toTaskOption() => - TaskOption(() async => Option.of(await run())); - - /// Convert this [Task] to [TaskEither]. - TaskEither toTaskEither() => - TaskEither(() 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>`. - /// - /// 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> traverseListWithIndex( - List list, - Task Function(A a, int i) f, - ) => - Task>( - () => Future.wait( - 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> traverseList( - List list, - Task Function(A a) f, - ) => - traverseListWithIndex(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>`. - /// - /// 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> traverseListWithIndexSeq( - List list, - Task Function(A a, int i) f, - ) => - Task>(() async { - List 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> traverseListSeq( - List list, - Task Function(A a) f, - ) => - traverseListWithIndexSeq(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_task} - /// Convert a `List>` to a single `Task>`. - /// - /// Each [Task] will be executed in parallel. - /// - /// If you need [Task] to be executed in sequence, use `sequenceListSeq`. - /// {@endtemplate} - static Task> sequenceList( - List> list, - ) => - traverseList(list, identity); - - /// {@template fpdart_sequence_list_seq_task} - /// Convert a `List>` to a single `Task>`. - /// - /// Each [Task] will be executed in sequence. - /// - /// If you need [Task] to be executed in parallel, use `sequenceList`. - /// {@endtemplate} - static Task> sequenceListSeq( - List> 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 { - final L value; - const _TaskEitherThrow(this.value); -} - -typedef DoAdapterTaskEither = Future Function(TaskEither); -DoAdapterTaskEither _doAdapter() => - (taskEither) => taskEither.run().then( - (either) => either.getOrElse((l) => throw _TaskEitherThrow(l)), - ); - -typedef DoFunctionTaskEither = Future Function( - DoAdapterTaskEither $); - -/// Tag the [HKT2] interface for the actual [TaskEither]. -abstract final class _TaskEitherHKT {} - -/// `TaskEither` 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 extends HKT2<_TaskEitherHKT, L, R> - with - Functor2<_TaskEitherHKT, L, R>, - Applicative2<_TaskEitherHKT, L, R>, - Monad2<_TaskEitherHKT, L, R>, - Alt2<_TaskEitherHKT, L, R> { - final Future> Function() _run; - - /// Build a [TaskEither] from a function returning a `Future>`. - const TaskEither(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory TaskEither.Do(DoFunctionTaskEither f) => TaskEither(() async { - try { - return Either.of(await f(_doAdapter())); - } on _TaskEitherThrow 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 flatMap(covariant TaskEither 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 bindEither(Either 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 chainEither(Either Function(R r) f) => flatMap( - (r) => f(r).toTaskEither(), - ); - - /// Returns a [TaskEither] that returns a `Right(a)`. - @override - TaskEither pure(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 map2( - covariant TaskEither 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 map3(covariant TaskEither m1, - covariant TaskEither 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 andThen(covariant TaskEither 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 map(C Function(R r) f) => ap(pure(f)); - - /// Change the value in the [Left] of [TaskEither]. - TaskEither mapLeft(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 bimap(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 ap(covariant TaskEither a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - /// Chain multiple functions having the same left type `L`. - @override - TaskEither call(covariant TaskEither chain) => - flatMap((_) => chain); - - /// Change this [TaskEither] from `TaskEither` to `TaskEither`. - TaskEither 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 alt(covariant TaskEither 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 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 orElse(TaskEither Function(L l) orElse) => - TaskEither(() async => (await run()).match( - (l) => orElse(l).run(), (r) => TaskEither.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 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 match(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 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 chainFirst( - covariant TaskEither Function(R b) chain, - ) => - flatMap((b) => chain(b).map((c) => b).orElse((l) => TaskEither.right(b))); - - /// Run the task and return a `Future>`. - Future> 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> 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 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 task) => - TaskEither(() async => Right(await task.run())); - - /// Build a [TaskEither] from the result of running `task`. - /// - /// Same of `TaskEither.rightTask` - factory TaskEither.fromTask(Task 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 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 option, L Function() onNone) => - TaskEither(() async => option.match( - () => Left(onNone()), - Right.new, - )); - - /// Build a [TaskEither] that returns `either`. - factory TaskEither.fromEither(Either 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 imperative(String str) async { - /// try { - /// return int.parse(str); - /// } catch (e) { - /// return -1; /// What does -1 means? 🤨 - /// } - /// } - /// - /// TaskEither functional(String str) { - /// return TaskEither.tryCatch( - /// () async => int.parse(str), - /// /// Clear error 🪄 - /// (error, stackTrace) => "Parsing error: $error", - /// ); - /// } - /// ``` - factory TaskEither.tryCatch(Future Function() run, - L Function(Object error, StackTrace stackTrace) onError) => - TaskEither(() async { - try { - return Right(await run()); - } catch (error, stack) { - return Left(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>`. - /// - /// 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> traverseListWithIndex( - List list, - TaskEither Function(A a, int i) f, - ) => - TaskEither>( - () async => Either.sequenceList( - await Task.traverseListWithIndex>( - 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> traverseList( - List list, - TaskEither Function(A a) f, - ) => - traverseListWithIndex(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_task_either} - /// Convert a `List>` to a single `TaskEither>`. - /// - /// Each [TaskEither] will be executed in parallel. - /// - /// If you need [TaskEither] to be executed in sequence, use `sequenceListSeq`. - /// {@endtemplate} - static TaskEither> sequenceList( - List> 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>`. - /// - /// 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> traverseListWithIndexSeq( - List list, - TaskEither Function(A a, int i) f, - ) => - TaskEither>( - () async => Either.sequenceList( - await Task.traverseListWithIndexSeq>( - 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> traverseListSeq( - List list, - TaskEither Function(A a) f, - ) => - traverseListWithIndexSeq(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_seq_task_either} - /// Convert a `List>` to a single `TaskEither>`. - /// - /// Each [TaskEither] will be executed in sequence. - /// - /// If you need [TaskEither] to be executed in parallel, use `sequenceList`. - /// {@endtemplate} - static TaskEither> sequenceListSeq( - List> list, - ) => - traverseListSeq(list, identity); - - /// {@macro fpdart_try_catch_task_either} - /// - /// It wraps the `TaskEither.tryCatch` factory to make chaining with `flatMap` - /// easier. - static TaskEither Function(T a) tryCatchK( - Future 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 Function(TaskOption); -Future _doAdapter(TaskOption taskOption) => taskOption.run().then( - (option) => option.getOrElse(() => throw const _TaskOptionThrow()), - ); - -typedef DoFunctionTaskOption = Future Function(DoAdapterTaskOption $); - -/// Tag the [HKT] interface for the actual [TaskOption]. -abstract final class _TaskOptionHKT {} - -/// `TaskOption` 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 extends HKT<_TaskOptionHKT, R> - with - Functor<_TaskOptionHKT, R>, - Applicative<_TaskOptionHKT, R>, - Monad<_TaskOptionHKT, R>, - Alt<_TaskOptionHKT, R> { - final Future> Function() _run; - - /// Build a [TaskOption] from a function returning a `Future>`. - const TaskOption(this._run); - - /// Initialize a **Do Notation** chain. - // ignore: non_constant_identifier_names - factory TaskOption.Do(DoFunctionTaskOption 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 flatMap(covariant TaskOption 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 pure(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 map2( - covariant TaskOption 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 map3(covariant TaskOption m1, - covariant TaskOption 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 andThen(covariant TaskOption Function() then) => - flatMap((_) => then()); - - /// Chain multiple [TaskOption] functions. - @override - TaskOption call(covariant TaskOption chain) => flatMap((_) => chain); - - /// If running this [TaskOption] returns [Some], then change its value from type `R` to - /// type `C` using function `f`. - @override - TaskOption map(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 ap(covariant TaskOption 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 alt(covariant TaskOption 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 orElse(TaskOption Function() orElse) => - TaskOption(() async => (await run()).match( - () => orElse().run(), - (r) => TaskOption.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 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 match(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 delay(Duration duration) => - TaskOption(() => Future.delayed(duration, run)); - - /// Run the task and return a `Future>`. - Future> 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 toTaskEither(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) => - 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 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 Function() run) => - TaskOption(() 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>`. - /// - /// 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> traverseListWithIndex( - List list, - TaskOption Function(A a, int i) f, - ) => - TaskOption>( - () async => Option.sequenceList( - await Task.traverseListWithIndex>( - 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> traverseList( - List list, - TaskOption Function(A a) f, - ) => - traverseListWithIndex(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_task_option} - /// Convert a `List>` to a single `TaskOption>`. - /// - /// Each [TaskOption] will be executed in parallel. - /// - /// If you need [TaskOption] to be executed in sequence, use `sequenceListSeq`. - /// {@endtemplate} - static TaskOption> sequenceList( - List> 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>`. - /// - /// 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> traverseListWithIndexSeq( - List list, - TaskOption Function(A a, int i) f, - ) => - TaskOption>( - () async => Option.sequenceList( - await Task.traverseListWithIndexSeq>( - 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> traverseListSeq( - List list, - TaskOption Function(A a) f, - ) => - traverseListWithIndexSeq(list, (a, _) => f(a)); - - /// {@template fpdart_sequence_list_seq_task_option} - /// Convert a `List>` to a single `TaskOption>`. - /// - /// Each [TaskOption] will be executed in sequence. - /// - /// If you need [TaskOption] to be executed in parallel, use `sequenceList`. - /// {@endtemplate} - static TaskOption> sequenceListSeq( - List> list, - ) => - traverseListSeq(list, identity); - - /// Build a [TaskOption] from `either` that returns [None] when - /// `either` is [Left], otherwise it returns [Some]. - static TaskOption fromEither(Either 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 Function(A a) tryCatchK( - Future 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 on HKT, Functor { - HKT alt(HKT Function() orElse); -} - -mixin Alt2 on HKT2, Functor2 { - HKT2 alt(HKT2 Function() orElse); -} - -mixin Alt3 on HKT3, Functor3 { - HKT3 alt(HKT3 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 on HKT, Functor { - HKT pure(B b); - HKT ap(HKT a); - - @override - HKT map(B Function(A a) f) => ap(pure(f)); -} - -mixin Applicative2 on HKT2, Functor2 { - HKT2 pure(C c); - HKT2 ap(HKT2 a); - - @override - HKT2 map(C Function(B a) f) => ap(pure(f)); -} - -mixin Applicative3 on HKT3, Functor3 { - HKT3 pure(D a); - HKT3 ap(HKT3 a); - - @override - HKT3 map(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 on Semigroup { - /// 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 instance(A Function(A a1, A a2) f) => _Band(f); -} - -class _Band with Semigroup, Band { - 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 on CommutativeMonoid, Semilattice { - /// Return a `BoundedSemilattice` that reverses the order. - @override - BoundedSemilattice 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 instance( - A emptyValue, A Function(A a1, A a2) f) => - _BoundedSemilattice(emptyValue, f); -} - -class _BoundedSemilattice - with - Semigroup, - Monoid, - CommutativeSemigroup, - Band, - Semilattice, - CommutativeMonoid, - BoundedSemilattice { - 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 on Group, CommutativeMonoid { - /// Create a `CommutativeGroup` instance from the given function, empty value, and inverse function. - static CommutativeGroup instance( - A emptyValue, A Function(A a1, A a2) f, A Function(A a) inv) => - _CommutativeGroup(emptyValue, f, inv); -} - -class _CommutativeGroup - with - Semigroup, - CommutativeSemigroup, - Monoid, - CommutativeMonoid, - Group, - CommutativeGroup { - 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 on Monoid, CommutativeSemigroup { - /// Return a `CommutativeMonoid` that reverses the order. - @override - CommutativeMonoid reverse() => - _CommutativeMonoid(empty, (x, y) => combine(y, x)); - - /// Create a `CommutativeMonoid` instance from the given function and empty value. - static CommutativeMonoid instance( - A emptyValue, A Function(A a1, A a2) f) => - _CommutativeMonoid(emptyValue, f); -} - -class _CommutativeMonoid - with - Semigroup, - CommutativeSemigroup, - Monoid, - CommutativeMonoid { - 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 on Semigroup { - /// Create a `CommutativeSemigroup` instance from the given function. - // ignore: library_private_types_in_public_api - static _CommutativeSemigroup instance(A Function(A a1, A a2) f) => - _CommutativeSemigroup(f); -} - -class _CommutativeSemigroup with Semigroup, CommutativeSemigroup { - 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 { - 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 and(Eq 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 or(Eq 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 xor(Eq 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( - /// (p) => p.value1, - /// ); - - /// /// Equality for of type [Parent] based on their `value2` ([double]). - /// final eqParentDouble = Eq.eqDouble.contramap( - /// (p) => p.value2, - /// ); - /// ``` - Eq contramap(T Function(A) map) => _Eq( - (a1, a2) => eqv(map(a1), map(a2)), - ); - - /// Convert an implicit `Eq` to an `Eq` using the given function `f`. - /// - /// ```dart - /// /// Convert an `Eq` on `int` to an `Eq` to check `String` length - /// final instance = Eq.instance((a1, a2) => a1 == a2); - /// final by = Eq.by((a) => a.length, instance); - /// - /// expect(by.eqv('abc', 'abc'), true); // Same length - /// expect(by.eqv('abc', 'ab'), false); // Different length - /// ``` - static Eq by(B Function(A a) f, Eq eq) => - _Eq((x, y) => eq.eqv(f(x), f(y))); - - /// Create an `Eq` instance from an `eqv` implementation. - /// - /// ```dart - /// final instance = Eq.instance((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 instance(bool Function(A a1, A a2) f) => _Eq(f); - - /// An `Eq` that delegates to universal equality (`==`). - static Eq fromUniversalEquals() => _Eq((x, y) => x == y); - - /// An `Eq` in which everything is the same - /// (calling `eqv` returns always `true`). - static Eq allEqual() => _Eq((x, y) => true); - - /// Instance of `Eq` for `num` using the standard `==` operator. - static Eq eqNum = _Eq((x, y) => x == y); - - /// Instance of `Eq` for `int` using the standard `==` operator. - static Eq eqInt = _Eq((x, y) => x == y); - - /// Instance of `Eq` for `double` using the standard `==` operator. - static Eq eqDouble = _Eq((x, y) => x == y); - - /// Instance of `Eq` for `String` using the standard `==` operator. - static Eq eqString = _Eq((x, y) => x == y); - - /// Instance of `Eq` for `bool` using the standard `==` operator. - static Eq eqBool = _Eq((x, y) => x == y); - - /// [Eq] instance to compare [DateTime] years. - static Eq dateEqYear = _Eq( - (a1, a2) => a1.year == a2.year, - ); - - /// [Eq] instance to compare [DateTime] months. - static Eq dateEqMonth = _Eq( - (a1, a2) => a1.month == a2.month, - ); - - /// [Eq] instance to compare [DateTime] days. - static Eq dateEqDay = _Eq( - (a1, a2) => a1.day == a2.day, - ); - - /// [Eq] instance to compare [DateTime] by year, month, and day. - static Eq dateEqYearMonthDay = _Eq( - (a1, a2) => - dateEqYear.eqv(a1, a2) && - dateEqMonth.eqv(a1, a2) && - dateEqDay.eqv(a1, a2), - ); -} - -class _Eq extends Eq { - 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 on HKT, Functor { - /// 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 extend(Z Function(HKT t) f); - - HKT> duplicate() => extend(identity); -} - -mixin Extend2 on HKT2, Functor2 { - /// Extend the type by applying function `f` to it. - HKT2 extend(Z Function(HKT2 t) f); - - HKT2> 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 on HKT, Functor { - /// Filter a data structure based on a boolean predicate. - HKT filter(bool Function(A a) f); - - /// Map over a data structure and filter based on a [Option] predicate. - HKT filterMap(Option Function(A a) f); - - /// Partition a data structure based on a boolean predicate. - Separated partition(bool Function(A a) f) => - (filter((a) => !f(a)), filter(f)); - - /// Partition a data structure based on an [Either] predicate. - Separated partitionMap(Either 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 on HKT { - B foldRight(B b, B Function(B acc, A a) f); - - B foldLeft(B b, B Function(B acc, A a) f) => - foldMap>(dualEndoMonoid(), (a) => (B b) => f(b, a))(b); - - /// Fold implemented by mapping `A` values into `B` and then - /// combining them using the given `Monoid` instance. - /// - /// Use `Monoid` 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(Monoid monoid, B Function(A a) f) => - foldRight(monoid.empty, (b, a) => monoid.combine(f(a), b)); - - B foldRightWithIndex(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 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 monoid) => foldMap(monoid, identity); - - HKT plus(HKT v); - - /// Insert a new element `A` at the beginning. - HKT prepend(A t); - - /// Insert a new element `A` at the end. - HKT append(A t); -} - -mixin Foldable2 on HKT2 { - C foldRight(C b, C Function(C acc, B b) f); - - C foldLeft(C b, C Function(C acc, B b) f) => - foldMap>(dualEndoMonoid(), (b) => (C c) => f(c, b))(b); - - C foldMap(Monoid monoid, C Function(B b) f) => - foldRight(monoid.empty, (c, b) => monoid.combine(f(b), c)); - - C foldRightWithIndex(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 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 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 extends HKT` to express the fact that the classes implementing -/// the [Functor] interface will need to be higher kinded types. -mixin Functor on HKT { - /// Return type is `HKT`, which expresses the fact that what - /// we return is using exactly the same type constructor represented by the brand `G` - HKT map(B Function(A a) f); -} - -mixin Functor2 on HKT2 { - HKT2 map(C Function(B b) f); -} - -mixin Functor3 on HKT3 { - HKT3 map(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 on Monoid { - /// 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 instance( - A emptyValue, A Function(A a1, A a2) f, A Function(A a) inv) => - _Group(emptyValue, f, inv); -} - -class _Group with Semigroup, Monoid, Group { - 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 extends Eq { - 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 fromUniversalHashCode() => - _Hash((x, y) => x == y, (x) => x.hashCode); -} - -class _Hash extends Hash { - 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`, we will write `HKT`. -/// This is useful because in the expression `HKT`, 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 { - const HKT(); -} - -abstract class HKT2 { - const HKT2(); -} - -abstract class HKT3 { - 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 on HKT, Applicative { - HKT flatMap(HKT Function(A a) f); - - @override - HKT ap(covariant Monad a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - HKT map2(Monad mc, D Function(A a, C c) f) => - flatMap((a) => mc.map((c) => f(a, c))); - - HKT map3( - Monad mc, Monad md, E Function(A a, C c, D d) f) => - flatMap((a) => mc.flatMap((c) => md.map((d) => f(a, c, d)))); - - HKT andThen(HKT Function() then) => flatMap((_) => then()); - - HKT chainFirst(covariant Monad Function(A a) chain) => - flatMap((a) => chain(a).map((b) => a)); - - HKT call(HKT chain) => flatMap((_) => chain); -} - -mixin Monad2 on HKT2, Applicative2 { - HKT2 flatMap(HKT2 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 ap(covariant Monad2 a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - HKT2 map2(Monad2 m1, D Function(B b, C c) f) => - flatMap((b) => m1.map((c) => f(b, c))); - - HKT2 map3(Monad2 m1, Monad2 m2, - E Function(B b, C c, D d) f) => - flatMap((b) => m1.flatMap((c) => m2.map((d) => f(b, c, d)))); - - HKT2 andThen(HKT2 Function() then) => - flatMap((_) => then()); - - HKT2 chainFirst( - covariant Monad2 Function(B b) chain) => - flatMap((b) => chain(b).map((c) => b)); - - HKT2 call(HKT2 chain) => flatMap((_) => chain); -} - -mixin Monad3 - on HKT3, Applicative3 { - HKT3 flatMap(HKT3 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 ap( - covariant Monad3 a) => - a.flatMap((f) => flatMap((v) => pure(f(v)))); - - HKT3 map2( - Monad3 m1, - N2 Function(P3, N1) f, - ) => - flatMap((b) => m1.map((c) => f(b, c))); - - HKT3 map3( - Monad3 m1, - Monad3 m2, - N3 Function(P3, N1, N2) f, - ) => - flatMap( - (b) => m1.flatMap((c) => m2.map((d) => f(b, c, d))), - ); - - HKT3 andThen( - HKT3 Function() then, - ) => - flatMap((_) => then()); - - HKT3 chainFirst( - covariant Monad3 Function(P3) chain, - ) => - flatMap((b) => chain(b).map((c) => b)); - - HKT3 call(HKT3 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`, -/// with `combine` as string concatenation, then `empty = ""`: -/// -/// ```dart -/// final instance = Monoid.instance('', (a1, a2) => '$a1$a2'); -/// -/// expect(instance.combine('abc', instance.empty), instance.combine(instance.empty, 'abc')); -/// expect(instance.combine('abc', instance.empty), 'abc'); -/// ``` -mixin Monoid on Semigroup { - /// Return the identity element for this monoid. - T get empty; - - /// Tests if `a` is the identity (`empty`). - bool isEmpty(T a, Eq 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 reverse() => _Monoid(empty, (x, y) => combine(y, x)); - - /// Create a `Monoid` instance from the given function and empty value. - static Monoid instance(A emptyValue, A Function(A a1, A a2) f) => - _Monoid(emptyValue, f); -} - -class _Monoid with Semigroup, Monoid { - 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> endoMonoid() => - Monoid.instance>(identity, (e1, e2) => (A a) => e1(e2(a))); - -Monoid> dualEndoMonoid() => - Monoid.instance>(identity, (e1, e2) => (A a) => e2(e1(a))); - -Monoid boolOrMonoid() => Monoid.instance(false, (a1, a2) => a1 || a2); -Monoid 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 extends PartialOrder { - 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( - /// (p) => p.value1, - /// ); - /// - /// /// Order values of type [Parent] based on their `value2` ([double]). - /// final orderParentDouble = Order.orderDouble.contramap( - /// (p) => p.value2, - /// ); - /// ``` - @override - Order contramap(T Function(A) map) => Order.from( - (a1, a2) => compare(map(a1), map(a2)), - ); - - /// Return an [Order] reversed. - Order 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` to an `Order` using the given - /// function `f`. - static Order by(B Function(A a) f, Order ord) => - _Order((x, y) => ord.compare(f(x), f(y))); - - /// Returns a new `Order` 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 whenEqual(Order first, Order second) => - _Order((x, y) { - final ord = first.compare(x, y); - return ord == 0 ? second.compare(x, y) : ord; - }); - - /// Define an `Order` using the given function `f`. - static Order from(int Function(A a1, A a2) f) => _Order(f); - - /// Define an `Order` using the given 'less than' function `f`. - static Order fromLessThan(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 allEqual() => _Order((x, y) => 0); - - /// Instance of `Order` for `int`. - static Order orderInt = _Order((x, y) => x == y - ? 0 - : x > y - ? 1 - : -1); - - /// Instance of `Order` for `num`. - static Order orderNum = _Order((x, y) => x == y - ? 0 - : x > y - ? 1 - : -1); - - /// Instance of `Order` for `double`. - static Order orderDouble = _Order((x, y) => x == y - ? 0 - : x > y - ? 1 - : -1); - - /// Instance of `Order` for [DateTime]. - static Order orderDate = _Order( - (a1, a2) => a1.compareTo(a2), - ); -} - -class _Order extends Order { - 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 extends Eq { - 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 contramap(T Function(A) map) => PartialOrder.from( - (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 by(B Function(A a) f, PartialOrder po) => - _PartialOrder((x, y) => po.partialCompare(f(x), f(y))); - - /// Defines a partial order on `A` from `p` where all arrows switch direction. - static PartialOrder reverse(PartialOrder p) => - _PartialOrder((x, y) => p.partialCompare(y, x)); - - /// Define a `PartialOrder[A]` using the given function `f`. - static PartialOrder from(double? Function(A a1, A a2) f) => - _PartialOrder(f); -} - -class _PartialOrder extends PartialOrder { - 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 { - /// Associative operation which combines two values. - /// - /// ```dart - /// final instance = Semigroup.instance((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((a1, a2) => '$a1$a2'); - /// final reverse = instance.reverse(); - /// - /// expect(reverse.combine('a', 'b'), 'ba'); - /// expect(reverse.combine('a', 'b'), instance.combine('b', 'a')); - /// ``` - Semigroup reverse() => _Semigroup((x, y) => combine(y, x)); - - /// Return a `Semigroup` which inserts `middle` between each pair of elements. - /// - /// ```dart - /// final instance = Semigroup.instance((a1, a2) => '$a1$a2'); - /// final intercalate = instance.intercalate('-'); - /// - /// expect(intercalate.combine('a', 'b'), 'a-b'); - /// expect(intercalate.combineN('a', 3), 'a-a-a'); - /// ``` - Semigroup intercalate(T middle) => - _Semigroup((x, y) => combine(x, combine(middle, y))); - - /// Create a `Semigroup` instance from the given function. - static Semigroup instance(A Function(A a1, A a2) f) => _Semigroup(f); - - /// Create a `Semigroup` instance that always returns the lefthand side. - static Semigroup first() => _Semigroup((x, y) => x); - - /// Create a `Semigroup` instance that always returns the righthand side. - static Semigroup last() => _Semigroup((x, y) => y); -} - -class _Semigroup with Semigroup { - 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 on Band, CommutativeSemigroup { - /// Given `Eq`, return a `PartialOrder` 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 asMeetPartialOrder(Eq 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`, return a `PartialOrder` 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 asJoinPartialOrder(Eq 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 instance(A Function(A a1, A a2) f) => - _Semilattice(f); -} - -class _Semilattice - with Semigroup, CommutativeSemigroup, Band, Semilattice { - 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 Function(A a); -typedef Separated = (HKT, HKT); From 30e643fbef28117bec0b61bf14626d6e3635fca4 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 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.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 future = Future.value('test'); - const testOption = const Option.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.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>.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 helloWorld1Fpdart(String message) => IO(() { - print("Hello World: $message"); - }); - -/// 2️⃣ Explicit error -/// Understand from the return type if and how the function may fail -Either Function() helloWorld2(String message) => () { - print("Hello World: $message"); - return Either.of(null); - }; - -/// A thunk with explicit error [Either] is called [IOEither] in `fpdart` -IOEither helloWorld2Fpdart1(String message) => IOEither(() { - print("Hello World: $message"); - return Either.of(null); - }); - -/// ...or using the `right` constructor -IOEither 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 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 helloWorld3Fpdart(String message) => - ReaderTaskEither((console) async { - console.log("Hello World: $message"); - return Either.of(null); - }); - -void main(List 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 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 - -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 id; - final Option birthDate; - final Option phone; - - const User({ - required this.id, - required this.birthDate, - required this.phone, - }); - - factory User.fromJson(Map json) => _$UserFromJson(json); - Map 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 - -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({ - "id": null, - "birthDate": null, - "phone": null, - }); - - expect(user.id, isA()); - expect(user.birthDate, isA()); - expect(user.phone, isA()); - }); - - test('fromJson (Some)', () { - final user = User.fromJson({ - "id": 1, - "birthDate": DateTime(2020), - "phone": "phone", - }); - - expect(user.id, isA>()); - 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, int>((s) => (s.first, s.sublist(1))); - final ap = state.flatMap( - (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 = fpdart.State; 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 locationSearch( - String query, - ) => - TaskEither.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>.safeCast( - json, - LocationInvalidMapFpdartFailure.new, - ), - ); - - final currentWeather = _( - data - .lookup('results') - .toEither(LocationKeyNotFoundFpdartFailure.new), - ); - - final results = _( - Either>.safeCast( - currentWeather, - LocationInvalidListFpdartFailure.new, - ), - ); - - final weather = _( - results.head.toEither(LocationDataNotFoundFpdartFailure.new), - ); - - return _( - Either.tryCatch( - () => Location.fromJson(weather as Map), - LocationFormattingFpdartFailure.new, - ), - ); - }), - ); - - /// Fetches [Weather] for a given [latitude] and [longitude]. - TaskEither getWeather({ - required double latitude, - required double longitude, - }) => - TaskEither.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, - String>(body, WeatherInvalidMapFpdartFailure.new), - ) - .chainEither( - (body) => body - .lookup('current_weather') - .toEither(WeatherKeyNotFoundFpdartFailure.new), - ) - .chainEither( - (currentWeather) => Either>.safeCast( - currentWeather, - WeatherInvalidListFpdartFailure.new, - ), - ) - .chainEither( - (results) => - results.head.toEither(WeatherDataNotFoundFpdartFailure.new), - ) - .chainEither( - (weather) => Either.tryCatch( - () => Weather.fromJson(weather as Map), - WeatherFormattingFpdartFailure.new, - ), - ); - - /// Verify that the response status code is 200, - /// and extract the response's body. - Either _validResponseBody( - http.Response response, - E Function(http.Response) onError, - ) => - Either.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 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 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 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); - } - - /// Fetches [Weather] for a given [latitude] and [longitude]. - Future 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; - - if (!bodyJson.containsKey('current_weather')) { - throw WeatherNotFoundFailure(); - } - - final weatherJson = bodyJson['current_weather'] as Map; - - 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()), - ); - }); - - 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()), - ); - }); - - 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()), - ); - }); - - 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() - .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()), - ); - }); - - 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()), - ); - }); - - 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() - .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( - Either result, { - dynamic Function(TypeMatcher)? typeMatch, -}) { - expect(result, isA>()); - result.match( - (l) => expect(l, typeMatch != null ? typeMatch(isA()) : isA()), - (_) => 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( - result, - typeMatch: (m) => m.having( - (failure) => failure.object, - 'Exception', - isA(), - ), - ); - }); - - 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( - 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( - 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( - 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(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( - 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(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(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>() - .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 @@ - - - - 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 @@ - - - - - - - - - - - - - - - - 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 @@ - - - - - - - - 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 @@ - - - - - - - - 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|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)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@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?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@Uy!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 zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ 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*%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&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^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 zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;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`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~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~;$%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`|?8ht^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%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(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 @@ - - - - - - - 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 @@ - - - - - - - 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 @@ - - - - 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 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 8.0 - - 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 = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 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 = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* 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 = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 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 = ""; - }; -/* 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 = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* 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 @@ - - - - - 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 @@ - - - - - IDEDidComputeMac32BitWarning - - - 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 @@ - - - - - PreviewsEnabled - - - 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 @@ - - - - - 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 @@ - - - - - IDEDidComputeMac32BitWarning - - - 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 @@ - - - - - PreviewsEnabled - - - 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@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 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`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}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?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 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 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)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 z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`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^;QgjgIfBDmcOgi5YDyu?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 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s 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 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s 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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 iW8Zci7-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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - 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 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - pokeapi_functional - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - 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 _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 _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 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, - ); - }, - (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 fetchPokemonFromUserInput(String pokemonId) => - TaskEither.Do((_) async { - final validPokemonId = await _(_validateUserPokemonId( - pokemonId, - ).toTaskEither()); - return _(fetchPokemon(validPokemonId)); - }); - -TaskEither 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 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 fetch(String pokemonId) async => _pokemonRequest( - () => fetchPokemonFromUserInput(pokemonId), - ); - - /// Generic private method to perform request and update the state. - Future _pokemonRequest( - TaskEither 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 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 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 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 toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $PokemonCopyWith 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 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 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 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 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 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 json) { - return _Sprite.fromJson(json); -} - -/// @nodoc -mixin _$Sprite { - @JsonKey(name: 'front_default') - String get frontDefault => throw _privateConstructorUsedError; - - Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) - $SpriteCopyWith 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 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 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 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~(l1AfjnEK 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*SYAX-%d(5gVjrHJWqXQshj@!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 @@ - - - - - - - - - - - - - - - - - pokeapi_functional - - - - - - - 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> readAsLines({ - Encoding encoding = utf8, - }); -} - -final class FileSystemLive extends FileSystem { - Effect> readAsLines({ - Encoding encoding = utf8, - }) => - Effect.env().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 searchWords, - Iterable<(String, String)> iterable, -) => - iterable.flatMapWithIndex( - (tuple, index) => searchWords.foldLeftWithIndex>( - [], - (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 searchWords, -}); - -final program = Effect>.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 - -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> 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? headers, -}) => - Response(''); - -TaskEither request() => TaskEither.tryCatch( - () async { - final Response getPrice = await post( - Uri.parse("URL"), - headers: { - 'Content-Type': 'application/json; charset=UTF-8', - }, - ); - - final Map json = - jsonDecode(getPrice.body) as Map; - - 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 makeRequest(String url) => - TaskEither.tryCatch( - () async => post( - Uri.parse(url), - headers: { - 'Content-Type': 'application/json; charset=UTF-8', - }, - ), - (error, stackTrace) => ApiRequestError(error, stackTrace), - ); - -Map mapToJson(Response response) => - jsonDecode(response.body) as Map; - -TaskEither> mappingRequest(String url) => - makeRequest(url).map(mapToJson); - -TaskEither validationRequest(Map json) => - !json.containsKey("pricing") - ? TaskEither.left(MissingPricingRequestError()) - : TaskEither.of(json["pricing"].toString()); - -TaskEither 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 getUsernameFromId(int id) => TaskEither.of('sandro'); -TaskEither getProfilePicture(String username) => - TaskEither.of('image'); -int getPictureWidth(String image) => 10; -TaskEither updatePictureWidth(int width) => TaskEither.of(true); - -Future getUsernameFromIdLinear(int id) async => 'sandro'; -Future getProfilePictureLinear(String username) async => 'image'; -int getPictureWidthLinear(String image) => 10; -Future updatePictureWidthLinear(int width) async => true; - -/// Linear (no fpdart) -Future changePictureSizeFromIdLinear(int id) async { - final username = await getUsernameFromIdLinear(id); - final image = await getProfilePictureLinear(username); - final width = getPictureWidthLinear(image); - return updatePictureWidthLinear(width); -} - -/// Chaining -TaskEither changePictureSizeFromId(int id) => - getUsernameFromId(id) - .flatMap((username) => getProfilePicture(username)) - .map((image) => getPictureWidth(image)) - .flatMap((width) => updatePictureWidth(width)); - -/// Do notation -TaskEither changePictureSizeFromIdDo(int id) => - TaskEither.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 map() => Option.of(10) - .map( - (a) => a + 1, - ) - .map( - (b) => b * 3, - ) - .map( - (c) => c - 4, - ); - -Option mapDo() => Option.Do((_) { - final a = _(Option.of(10)); - final b = a + 1; - final c = b * 3; - return c - 4; - }); - -/// [flatMap]: Chain [Option] -Option flatMap() => Option.of(10) - .flatMap( - (a) => Option.of(a + 1), - ) - .flatMap( - (b) => Option.of(b * 3), - ) - .flatMap( - (c) => Option.of(c - 4), - ); - -Option 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 andThen() => Option.of(10).andThen(() => Option.of(20)); -Option 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.tryCatch( () => Future.value(10), (error, stackTrace) => "10", ); - final effect1 = Effect.function(() => 10); + final effect1 = Effect.function(() => 10); - final doing = Effect.gen( + final main = Effect.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(10).provide()); final asOption = await _(Some(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 lines; - - OutputEvent(this.level, this.lines); -} - -abstract class LogFilter { - bool shouldLog(LogEvent logEvent); -} - -abstract class LogPrinter { - List 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 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>.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.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 b = none(); - - /// 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 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(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(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.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.of(10); - - /// Create an instance of [Left] - final left = Either.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' in type cast - final waitWhat = intValue as List; - final first = waitWhat.first; - print(first); - - /// Safe 🎯 - final wellYeah = Either>.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) { - 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 setCart(String uid, Cart cart) { - return Future.value(); - } -} - -class OrdersRepository { - Future addOrder(String uid, Order order) { - return Future.value(); - } -} - -final cartRepository = CartRepository(); -final authRepository = AuthRepository(); -final ordersRepository = OrdersRepository(); - -Future 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 list) { - if (index < 0 || index >= list.length) { - return null; - } - - return list[index]; -} - -Either getEither(int index, List 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 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 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.of(10); - - /// Create an instance of [Left] - final left = Either.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 buyBanana() => getRandomEither('🍌', "We got no 🍌"); - Either buyApple() => getRandomEither('🍎', "We got no 🍎"); - Either buyPear() => getRandomEither('🍐', "We got no 🍐"); - - Either buyAmount() => - getRandomEither(randomInt(1, 10).run(), "Empty 💁🏼‍♂️"); -} - -Either getRandomEither(R right, L left) => randomBool - .map>( - (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 goToShoppingCenter() => - getRandomEither(const Market(), "Shopping center closed ☝️"); -Either 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.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(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 main() async { - final either = Either.of(10); - - /// Without using `identity`, you must write a function to return - /// the input parameter `(l) => l`. - final noId = either.match((l) => l, (r) => '$r'); - - /// Using `identity`/`id`, the function just returns its input parameter. - final withIdentity = either.match(identity, (r) => '$r'); - - /// 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 main() async { - /// Create instance of [IO] from a value - final IO io = IO.of(10); - - /// Create instance of [IO] from a sync function - final ioRun = IO(() => 10); - - /// Map [int] to [String] - final IO 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(0, (b, t) { - print("([$b] - [$t])"); - return b - t; - }); - print('== $fl'); - - print('--- Visualize fold right'); - final fr = list.foldRight(0, (b, t) { - print("([$b] - [$t])"); - return b - t; - }); - print('== $fr'); - - print('--- Visualize fold left with index'); - final fli = list.foldLeftWithIndex(0, (b, t, i) { - print("([$b] - [$t] - [$i])"); - return b - t - i; - }); - print('== $fli'); - - print('--- Visualize fold right with index'); - final fri = list.foldRightWithIndex(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 = {} - .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 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 args) { - int? nullableInt = 10; - if (nullableInt == null) { - print("Missing ‼️"); - } else { - print("Found $nullableInt 🎯"); - } - - /// 👆 Exactly the same as 👇 - - Option 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 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'. - optionStr.toLowerCase; - - /// Option has methods that makes it more powerful (chain methods) ⛓ - String? strNullable = Random().nextBool() ? "string" : null; - Option optionNullable = some("string"); - - /// Declarative API: more readable and composable 🎉 - Option 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`, not `Some` 😐 - optionIntNullable; - } - - if (strNullable != null) { - /// This is now `String` 🤝 - strNullable; - } - - List? 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 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 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 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 map = a.map( - (a) => (int b) => sumToDouble(a, b), - ); - - /// Using `ap`, we get the final `Option` 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 stringToInt(String a) => Option.fromPredicateMap( - a, - (str) => str.isNotEmpty, - (str) => str.length, - ); - -Option intToDouble(int a) => - Option.fromPredicateMap(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 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 divideF(int x, int y) { - if (y == 0) { - return none(); - } - return some(x ~/ y); -} - -/// Error handling with exceptions using [Option] 🎉 -Option 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(); - - /// Create an instance of [Some] - final option = Option.of(10); - - /// Create an instance of [None] - final noneInit3 = Option.none(); - - /// If the predicate is `true`, then [Some], otherwise [None] - final predicate = Option.fromPredicate(10, (a) => a > 5); - - /// If no exception, then [Some], otherwise [None] - final tryCatchInit = Option.tryCatch(() => int.parse('10')); - - /// When the value is not `null`, then [Some], otherwise [None] - final nullable = Option.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 buyBanana() => getRandomOption('🍌'); - Option buyApple() => getRandomOption('🍎'); - Option buyPear() => getRandomOption('🍐'); - - Option buyAmount() => getRandomOption(randomInt(1, 10).run()); -} - -Option getRandomOption(T value) => randomBool - .map( - (isValid) => isValid ? some(value) : none(), - ) - .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 goToShoppingCenter() => getRandomOption(const Market()); -Option 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((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 modifyInput(List 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) => '$message'; -} - -class ItalicPrinter implements Printer { - @override - String write(String message) => '$message'; -} - -/// 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 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); // -> name - - /// 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); // -> name - - /// 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 render3 = printing3('name'); - final Reader map = render3.map((a) => a.length); - - /// Reader allows dependency injection - final String result3a = render3.run(BoldPrinter()); - final int result3b = map.run(ItalicPrinter()); - print(result3a); // -> name - 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 cReader() => Reader((dependency) { - dependency.logger('Current environment: ${dependency.environment}'); - return 1; - }); -Reader bReader() => cReader().map((a) => a * 2); -Reader 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 args) async { - final rte = ReaderTaskEither.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]. -typedef Stack = List; - -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 popState() => State( - (s) => (s.last, s.sublist(0, s.length - 1)), - ); - -State 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 add10(int previous) async => previous + 10; - -Future main() async { - final stateAsync = StateAsync( - (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 asyncI() { - return Future.value(10).then((value) => value * 10); -} - -/// No need of `async`, you decide when to run the [Future] ⚡ -Task asyncF() { - return Task(() async => 10).map((a) => a * 10); -} - -Future main() async { - /// Create instance of [Task] from a value - final Task 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 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 getUsername() => Future.value("Sandro"); -Future getEncodedName() => Future.value(10); - -Future getEmail() => Future.value("@"); - -Future sendInformation(String usernameOrName, String email) => - Future.value(true); - -Future 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 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 getTask() => Task(() async { - print("I am running [Task]..."); - return 10; - }); - -Future getFuture() async { - print("I am running [Future]..."); - return 10; -} - -void main() { - Task taskInt = getTask(); - Future futureInt = getFuture(); - - // Future 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> getStudents = TaskEither.tryCatch( - () => StudentRepo.getAllStudents(), - (_, __) => StudentFailure(), -); - -TaskEither> getCoursesOfStudents( - List 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>` - final taskEitherRequest = getStudents.flatMap(getCoursesOfStudents); - - /// In case of error map `ApiFailure` to `String` using `logFailure` - /// - /// Type: `TaskEither>` - final taskRequest = taskEitherRequest.mapLeft(logFailure); - - /// Run everything at the end! - /// - /// Type: `Either>` - 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> getAllStudents() async => [ - Student("Juan"), - Student("Maria"), - ]; - - static Future> getAllCourses(List 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 resendVerificationEmail = - TaskEither.of("done"); - -TaskEither registerAnalytics = TaskEither.of(1); - -Future 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 apiRequestMock() => Future.value(10); - -/// Imperative code -/// -/// `try` - `catch` - `finally` -Future imperative() async { - try { - final response = await apiRequestMock(); - print(response); - } catch (e) { - print("Error: $e"); - } finally { - print("Complete!"); - } -} - -/// Functional code -/// -/// `tryCatch` -Future functional() async { - final task = TaskEither.tryCatch( - apiRequestMock, - (e, _) => "Error: $e", - ).match( - (l) { - print(l); - return unit; - }, - (r) { - print(r); - return unit; - }, - ).chainFirst( - (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 imperative(String str) async { - try { - return int.parse(str); - } catch (e) { - return -1; // What does -1 means? 🤨 - } -} - -TaskEither functional(String str) { - return TaskEither.tryCatch( - () async => int.parse(str), - // Clear error 🪄 - (error, stackTrace) => "Parsing error: $error", - ); -} - -/// What error is that? What is [dynamic]? -Future asyncI() { - return Future.error('Some error!') - .then((value) => value * 10) - .catchError( - (dynamic error) { - print(error); - return 0; - }, - ); -} - -/// Handle all the errors easily ✨ -TaskEither asyncF() { - return TaskEither( - () async => left('Some error'), - ).map((r) => r * 10); -} - -// Methods 👇 - -TaskEither mapLeftExample(TaskEither taskEither) => - taskEither.mapLeft( - (string) => string.length, - ); - -TaskEither bimapExample(TaskEither taskEither) => - taskEither.bimap( - (string) => string.length, - (number) => number / 2, - ); - -TaskEither toTaskEitherExample(Either taskEither) => - taskEither.toTaskEither(); - -/// Chain [Either] to [TaskEither] -TaskEither binding = - TaskEither.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 everythingIsFine(int a) async => a + 42; - -Future sendComplainRequest(String a) async => - '$a - What data is that!!!'; - -Either 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?> example; - -final taskOp = TaskOption.flatten( - (TaskOption.fromTask( - Task?>( - () => example, - ).map( - (ex) => Option.fromNullable(ex).toTaskOption(), - ), - )), -); - -/// Using `Option.fromNullable`, the [Future] cannot fail -final taskOpNoFail = TaskOption>( - () async => Option.fromNullable(await example), -); - -/// Using `Option.fromNullable` when the [Future] can fail -final taskOpFail = TaskOption?>.tryCatch( - () => example, -).flatMap>( - (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 toTaskOptionExample(Option 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( - (p) => p.value1, - ); - - /// Equality for of type [Parent] based on their `value2` ([double]). - final eqParentDouble = Eq.eqDouble.contramap( - (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 { - const _GreekLetterOrder(); - - @override - int compare(GreekLetter a, GreekLetter b) => - _internalValue[a]! - _internalValue[b]!; - - static const _internalValue = { - 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( - (p) => p.value1, - ); - - /// Order values of type [Parent] based on their `value2` ([double]). - final orderParentDouble = Order.orderDouble.contramap( - (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 { typedef DoAdapterEffect = Future Function(IEffect); -DoAdapterEffect _doAdapter(E env) => (effect) => Future.sync( +DoAdapterEffect _doAdapter(E? env) => (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 { } final class Effect extends IEffect { - final FutureOr> 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> Function(E? env) _unsafeRun; + const Effect._(this._unsafeRun); @override @@ -43,7 +47,7 @@ final class Effect extends IEffect { } /// {@category execution} - Future> _runEffect(E env) async => _unsafeRun(env); + Future> _runEffect(E? env) async => _unsafeRun(env); /// {@category execution} Future> call(E env) => _runEffect(env); @@ -117,12 +121,12 @@ final class Effect extends IEffect { /// /// {@category do_notation} Effect provide(E Function(V env) f) => Effect._( - (env) => _unsafeRun(f(env)), + (env) => _unsafeRun(f(env!)), ); /// {@category do_notation} static Effect env() => Effect._( - (env) async => Exit.success(env), + (env) async => Exit.success(env!), ); /// {@category combining} @@ -210,3 +214,12 @@ final class Effect extends IEffect { }, ); } + +extension ProvideNever on Effect { + /// Add a required dependency instead of [Never]. + /// + /// {@category do_notation} + Effect withEnv() => 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((a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA()); - }); - }); - - test('combineN', () { - final instance = Band.instance((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(0, (a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA()); - }); - - test('Band', () { - expect(instance, isA()); - }); - - test('Semilattice', () { - expect(instance, isA()); - }); - - test('CommutativeSemigroup', () { - expect(instance, isA()); - }); - - test('Monoid', () { - expect(instance, isA()); - }); - - test('CommutativeMonoid', () { - expect(instance, isA()); - }); - }); - - test('combineN', () { - final instance = BoundedSemilattice.instance(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(0, (a1, a2) => a1 + a2, (a) => -a); - - test('Semigroup', () { - expect(instance, isA()); - }); - - test('CommutativeSemigroup', () { - expect(instance, isA()); - }); - - test('CommutativeMonoid', () { - expect(instance, isA()); - }); - - test('Monoid', () { - expect(instance, isA()); - }); - - test('Group', () { - expect(instance, isA()); - }); - }); - }); -} 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(0, (a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA()); - }); - - test('CommutativeSemigroup', () { - expect(instance, isA()); - }); - - test('Monoid', () { - expect(instance, isA()); - }); - }); - }); -} 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((a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA()); - }); - }); - }); -} 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().test('dateOrder', (d1, d2) { - final compare = Order.orderDate.compare(d1, d2); - expect( - compare, - d1.isAfter(d2) - ? 1 - : d1.isBefore(d2) - ? -1 - : 0); - }); - - Glados2().test('dateEqYear', (d1, d2) { - final compare = Eq.dateEqYear.eqv(d1, d2); - expect(compare, d1.year == d2.year); - }); - - Glados2().test('dateEqMonth', (d1, d2) { - final compare = Eq.dateEqMonth.eqv(d1, d2); - expect(compare, d1.month == d2.month); - }); - - Glados2().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().test('eqvYear', (d1, d2) { - final compare = d1.eqvYear(d2); - expect(compare, d1.year == d2.year); - }); - - Glados2().test('eqvMonth', (d1, d2) { - final compare = d1.eqvMonth(d2); - expect(compare, d1.month == d2.month); - }); - - Glados2().test('eqvDay', (d1, d2) { - final compare = d1.eqvDay(d2); - expect(compare, d1.day == d2.day); - }); - - Glados2().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>()); - expect(castString, isA>()); - }); - }); - }); - - group('is a', () { - final either = Either.of(10); - - test('Monad', () { - expect(either, isA()); - }); - - test('Applicative', () { - expect(either, isA()); - }); - - test('Functor', () { - expect(either, isA()); - }); - - test('Foldable', () { - expect(either, isA()); - }); - - test('Alt', () { - expect(either, isA()); - }); - - test('Extend', () { - expect(either, isA()); - }); - }); - - group('map', () { - test('Right', () { - final value = Either.of(10); - final map = value.map((a) => a + 1); - map.matchTestRight((r) { - expect(r, 11); - }); - }); - - test('Left', () { - final value = Either.left('abc'); - final map = value.map((a) => a + 1); - map.matchTestLeft((l) => expect(l, 'abc')); - }); - }); - - group('bimap', () { - test('Right', () { - final value = Either.of(10); - final map = value.bimap((l) => "none", (a) => a + 1); - map.matchTestRight((r) { - expect(r, 11); - }); - }); - - test('Left', () { - final value = Either.left('abc'); - final map = value.bimap((l) => "none", (a) => a + 1); - map.matchTestLeft((l) => expect(l, 'none')); - }); - }); - - group('map2', () { - test('Right', () { - final value = Either.of(10); - final map = value.map2( - Either.of(1.5), (a, b) => a + b); - map.match((_) { - fail('should be right'); - }, (r) => expect(r, 11.5)); - }); - - test('Left', () { - final value = Either.left('none'); - final map = value.map2( - Either.of(1.5), (a, b) => a + b); - map.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('map3', () { - test('Right', () { - final value = Either.of(10); - final map = value.map3( - Either.of(1.5), - Either.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.left('none'); - final map = value.map3( - Either.of(1.5), - Either.of(1.5), - (a, b, c) => a + b + c); - map.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - test('pure', () { - final value = Either.of(10); - final pure = value.pure('abc'); - pure.match((_) { - fail('should be right'); - }, (r) => expect(r, 'abc')); - }); - - group('mapLeft', () { - test('Right', () { - final value = Either.of(10); - final map = value.mapLeft((a) => 'pre-$a'); - map.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final value = Either.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.of(10); - final fold = value.foldRight(10, (a, b) => a + b); - expect(fold, 20); - }); - - test('Left', () { - final value = Either.left('abc'); - final fold = value.foldRight(10, (a, b) => a + b); - expect(fold, 10); - }); - }); - - group('foldLeft', () { - test('Right', () { - final value = Either.of(10); - final fold = value.foldLeft(10, (a, b) => a + b); - expect(fold, 20); - }); - - test('Left', () { - final value = Either.left('abc'); - final fold = value.foldLeft(10, (a, b) => a + b); - expect(fold, 10); - }); - }); - - group('foldRightWithIndex', () { - test('Right', () { - final value = Either.of(10); - final fold = value.foldRightWithIndex(10, (i, a, b) => a + b); - expect(fold, 20); - }); - - test('Left', () { - final value = Either.left('abc'); - final fold = value.foldRightWithIndex(10, (i, a, b) => a + b); - expect(fold, 10); - }); - }); - - group('foldLeftWithIndex', () { - test('Right', () { - final value = Either.of(10); - final fold = value.foldLeftWithIndex(10, (i, a, b) => a + b); - expect(fold, 20); - }); - - test('Left', () { - final value = Either.left('abc'); - final fold = value.foldLeftWithIndex(10, (i, a, b) => a + b); - expect(fold, 10); - }); - }); - - group('foldMap', () { - test('Right', () { - final value = Either.of(10); - final fold = value.foldMap( - Monoid.instance(0, (a1, a2) => a1 + a2), (a) => a); - expect(fold, 10); - }); - - test('Left', () { - final value = Either.left('abc'); - final fold = value.foldMap( - Monoid.instance(0, (a1, a2) => a1 + a2), (a) => a); - expect(fold, 0); - }); - }); - - group('ap', () { - test('Right', () { - final value = Either.of(10); - final ap = value.ap(Either.of((n) => n + 1)); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 11)); - }); - - test('Left', () { - final value = Either.of(10); - final ap = value.ap(Either.left('none')); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('alt', () { - test('Right', () { - final value = Either.of(10); - final ap = value.alt(() => Either.of(0)); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.alt(() => Either.of(0)); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 0)); - }); - }); - - group('extend', () { - test('Right', () { - final value = Either.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.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.of(10); - final ap = value.duplicate(); - expect(ap, isA>>()); - ap.match((_) { - fail('should be right'); - }, - (r) => r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10))); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.duplicate(); - expect(ap, isA>>()); - ap.match( - (l) => expect(l, 'none'), - (r) => r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - })); - }); - }); - - group('length', () { - test('Right', () { - final value = Either.of(10); - expect(value.length(), 1); - }); - - test('Left', () { - final value = Either.left('none'); - expect(value.length(), 0); - }); - }); - - group('concatenate', () { - test('Right', () { - final value = Either.of(10); - final ap = value.concatenate(Monoid.instance(0, (a1, a2) => a1 + a2)); - expect(ap, 10); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.concatenate(Monoid.instance(0, (a1, a2) => a1 + a2)); - expect(ap, 0); - }); - }); - - group('any', () { - test('Right (true)', () { - final value = Either.of(10); - final ap = value.any((a) => a > 5); - expect(ap, true); - }); - - test('Right (false)', () { - final value = Either.of(10); - final ap = value.any((a) => a < 5); - expect(ap, false); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.any((a) => a > 5); - expect(ap, false); - }); - }); - - group('all', () { - test('Right (true)', () { - final value = Either.of(10); - final ap = value.all((a) => a > 5); - expect(ap, true); - }); - - test('Right (false)', () { - final value = Either.of(10); - final ap = value.all((a) => a < 5); - expect(ap, false); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.all((a) => a > 5); - expect(ap, true); - }); - }); - - group('filterOrElse', () { - test('Right (true)', () { - final value = Either.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.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.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.of(10); - final ap = value.flatMap((a) => Right('$a')); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, '10')); - }); - - test('then Left', () { - final value = Either.of(10); - final ap = - value.flatMap((a) => Either.left('none')); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('Left', () { - test('then Right', () { - final value = Either.left('0'); - final ap = - value.flatMap((a) => Either.of('$a')); - ap.match((l) => expect(l, '0'), (_) { - fail('should be left'); - }); - }); - - test('then Left', () { - final value = Either.left('0'); - final ap = - value.flatMap((a) => Either.left('none')); - ap.match((l) => expect(l, '0'), (_) { - fail('should be left'); - }); - }); - }); - }); - - group('toOption', () { - test('Right', () { - final value = Either.of(10); - final ap = value.toOption(); - ap.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.toOption(); - expect(ap, isA()); - }); - }); - - group('toNullable', () { - test('Right', () { - final value = Either.of(10); - final ap = value.toNullable(); - expect(ap, 10); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.toNullable(); - expect(ap, null); - }); - }); - - group('toIOEither', () { - test('Right', () { - final value = Either.of(10); - final ap = value.toIOEither(); - final result = ap.run(); - expect(result, value); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.toIOEither(); - final result = ap.run(); - expect(result, value); - }); - }); - - group('toTaskEither', () { - test('Right', () async { - final value = Either.of(10); - final ap = value.toTaskEither(); - final result = await ap.run(); - expect(result, value); - }); - - test('Left', () async { - final value = Either.left('none'); - final ap = value.toTaskEither(); - final result = await ap.run(); - expect(result, value); - }); - }); - - group('isLeft', () { - test('Right', () { - final value = Either.of(10); - final ap = value.isLeft(); - expect(ap, false); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.isLeft(); - expect(ap, true); - }); - }); - - group('isRight', () { - test('Right', () { - final value = Either.of(10); - final ap = value.isRight(); - expect(ap, true); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.isRight(); - expect(ap, false); - }); - }); - - group('getLeft', () { - test('Right', () { - final value = Either.of(10); - final ap = value.getLeft(); - expect(ap, isA()); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.getLeft(); - ap.matchTestSome((t) { - expect(t, 'none'); - }); - }); - }); - - group('getRight', () { - test('Right', () { - final value = Either.of(10); - final ap = value.getRight(); - ap.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.getRight(); - expect(ap, isA()); - }); - }); - - group('getOrElse', () { - test('Right', () { - final value = Either.of(10); - final ap = value.getOrElse((l) => -1); - expect(ap, 10); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.getOrElse((l) => -1); - expect(ap, -1); - }); - }); - - group('match', () { - test('Right', () { - final value = Either.of(10); - final ap = value.match((l) => -1, (r) => 1); - expect(ap, 1); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.match((l) => -1, (r) => 1); - expect(ap, -1); - }); - }); - - group('elem', () { - test('Right (true)', () { - final value = Either.of(10); - final ap = value.elem(10, Eq.instance((a1, a2) => a1 == a2)); - expect(ap, true); - }); - - test('Right (false)', () { - final value = Either.of(10); - final ap = value.elem(0, Eq.instance((a1, a2) => a1 == a2)); - expect(ap, false); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.elem(10, Eq.instance((a1, a2) => a1 == a2)); - expect(ap, false); - }); - }); - - group('exists', () { - test('Right (true)', () { - final value = Either.of(10); - final ap = value.exists((r) => r > 5); - expect(ap, true); - }); - - test('Right (false)', () { - final value = Either.of(10); - final ap = value.exists((r) => r < 5); - expect(ap, false); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.exists((r) => r > 5); - expect(ap, false); - }); - }); - - group('swap', () { - test('Right', () { - final value = Either.of(10); - final ap = value.swap(); - ap.match((l) => expect(l, 10), (_) { - fail('should be left'); - }); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.swap(); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 'none')); - }); - }); - - group('flatten', () { - test('Right Right', () { - final value = Either>.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>.of(Either.left('none')); - final ap = Either.flatten(value); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('Left', () { - final value = Either>.left('none'); - final ap = Either.flatten(value); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('orElse', () { - test('Right', () { - final value = Either.of(10); - final ap = value.orElse((l) => Either.of(0)); - ap.match((l) { - fail('should be right'); - }, (r) { - expect(r, 10); - }); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.orElse((l) => Either.of(0)); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, 0)); - }); - }); - - group('andThen', () { - test('Right', () { - final value = Either.of(10); - final ap = value.andThen(() => Either.of('10')); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, '10')); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value.andThen(() => Either.of('10')); - ap.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('call', () { - test('Right', () { - final value = Either.of(10); - final ap = value(Either.of('10')); - ap.match((_) { - fail('should be right'); - }, (r) => expect(r, '10')); - }); - - test('Left', () { - final value = Either.left('none'); - final ap = value(Either.of('10')); - ap.match((l) { - expect(l, 'none'); - }, (_) { - fail('should be left'); - }); - }); - }); - - test('of()', () { - final value = Either.of(10); - expect(value, isA()); - value.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('right()', () { - final value = Either.right(10); - expect(value, isA()); - value.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('of() == right()', () { - final of = Either.of(10); - final right = Either.right(10); - expect(of, right); - }); - - test('left()', () { - final value = Either.left('none'); - expect(value, isA()); - 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.none(); - final either = Either.fromOption(value, () => 'none'); - either.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('fromPredicate', () { - test('Right', () { - final either = - Either.fromPredicate(10, (v) => v > 5, (_) => 'none'); - either.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final either = - Either.fromPredicate(10, (v) => v < 5, (_) => 'none'); - either.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('fromNullable', () { - test('Right', () { - final either = Either.fromNullable(10, () => 'none'); - either.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final either = Either.fromNullable(null, () => 'none'); - either.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('tryCatch', () { - test('Right', () { - final either = Either.tryCatch( - () => int.parse('10'), (o, s) => 'none'); - either.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final either = Either.tryCatch( - () => int.parse('invalid'), (o, s) => 'none'); - either.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - }); - - group('tryCatchK', () { - test('Right', () { - final either = Either.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.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( - Eq.instance((a1, a2) => a1 == a2), Eq.instance((a1, a2) => a1 == a2)); - final eitherR = Either.of(10); - final eitherL = Either.left('none'); - expect(eq.eqv(eitherR, eitherR), true); - expect(eq.eqv(eitherR, Either.of(10)), true); - expect(eq.eqv(eitherR, Either.of(9)), false); - expect(eq.eqv(eitherR, Either.left('none')), false); - expect(eq.eqv(eitherL, eitherL), true); - expect(eq.eqv(eitherL, Either.left('none')), true); - expect(eq.eqv(eitherL, Either.left('error')), false); - }); - - test('getSemigroup', () { - final sg = Either.getSemigroup( - Semigroup.instance((a1, a2) => a1 + a2)); - final eitherR = Either.of(10); - final eitherL = Either.left('none'); - expect(sg.combine(eitherR, eitherR), Either.of(20)); - expect(sg.combine(eitherR, eitherL), eitherR); - expect(sg.combine(eitherL, eitherR), eitherR); - expect(sg.combine(eitherL, Either.left('error')), eitherL); - }); - - test('Right value', () { - const r = Right(10); - expect(r.value, 10); - }); - - test('Left value', () { - const l = Left('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(1), - left("Error"), - right(3), - right(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(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( - 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( - 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( - list, - (a, i) => i % 2 == 0 ? right("$a$i") : left("Error"), - ); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - test('Right == Right', () { - final r1 = Either.of(10); - final r2 = Either.of(9); - final r3 = Either.of(8.0); - final r4 = Either.of(10); - final r5 = Either.of(10.0); - final l1 = Either.left('none'); - final l2 = Either.left('error'); - final map1 = {'m1': r1, 'm2': r1}; - final map2 = {'m1': r1, 'm2': r2}; - final map3 = {'m1': r1, 'm2': r4}; - final map4 = {'m1': r1, 'm2': r3}; - final map5 = {'m1': r1, 'm2': r5}; - final map6 = {'m1': r1, 'm2': r1}; - final map7 = {'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.of(10); - final l1 = Either.left('none'); - final l2 = Either.left('error'); - final l3 = Either.left('none'); - final l4 = Either.left(1.0); - final map1 = {'m1': l1, 'm2': l1}; - final map2 = {'m1': l1, 'm2': l3}; - final map3 = {'m1': l1, 'm2': l2}; - final map4 = {'m1': l1, 'm2': l4}; - final map5 = {'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.of(10); - expect(value.toString(), 'Right(10)'); - }); - - test('Left', () { - final value = Either.left('none'); - expect(value.toString(), 'Left(none)'); - }); - }); - }); - - group('bind', () { - test('Right', () { - final either1 = Either.of(10); - final result = either1.bind((r) => Either.of(r + 10)); - expect(result.getOrElse((l) => 0), 20); - }); - - test('Left', () { - final either1 = Either.left('String'); - final result = either1.bind((r) => Either.of(r + 10)); - expect(result.getOrElse((l) => 0), 0); - expect(result.getLeft().getOrElse(() => ''), 'String'); - }); - }); - - group('bindFuture', () { - test('Right', () async { - final either1 = Either.of(10); - final asyncEither = either1.bindFuture((r) async => Either.of(r + 10)); - final result = await asyncEither.run(); - expect(result.getOrElse((l) => 0), 20); - }); - - test('Left', () async { - final either1 = Either.left('String'); - final asyncEither = either1.bindFuture((r) async => Either.of(r + 10)); - final result = await asyncEither.run(); - expect(result.getOrElse((l) => 0), 0); - expect(result.getLeft().getOrElse(() => ''), 'String'); - }); - }); - - test('chainFirst', () { - final either = Either.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(1), - right(2), - left('a'), - left('b'), - right(3), - ]; - final result = Either.rights(list); - expect(result, [1, 2, 3]); - }); - - test('lefts', () { - final list = [ - right(1), - right(2), - left('a'), - left('b'), - right(3), - ]; - final result = Either.lefts(list); - expect(result, ['a', 'b']); - }); - - test('partitionEithers', () { - final list = [ - right(1), - right(2), - left('a'), - left('b'), - right(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>()); - expect(castString, isA>()); - }); - - test('Right', () { - final cast = Either.safeCast(10, (value) => 'Error'); - cast.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () { - final cast = Either.safeCast('abc', (value) => 'Error'); - cast.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('safeCastStrict', () { - test('Right', () { - final cast = - Either.safeCastStrict(10, (value) => 'Error'); - cast.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () { - final cast = - Either.safeCastStrict('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.Do((_) { - final a = _(Either.of(10)); - final b = _(Either.of(5)); - final c = _(Either.left('Error')); - return a + b + c; - }); - doEither.matchTestLeft((t) { - expect(t, 'Error'); - }); - }); - - test('should rethrow if throw is used inside Do', () { - final doEither = () => Either.Do((_) { - _(Either.of(10)); - throw UnimplementedError(); - }); - - expect(doEither, throwsA(const TypeMatcher())); - }); - - test('should rethrow if Left is thrown inside Do', () { - final doEither = () => Either.Do((_) { - _(Either.of(10)); - throw Left('Error'); - }); - - expect(doEither, throwsA(const TypeMatcher())); - }); - - test('should no execute past the first Left', () { - var mutable = 10; - final doEitherLeft = Either.Do((_) { - final a = _(Either.of(10)); - final b = _(Either.left("Error")); - mutable += 10; - return a + b; - }); - - expect(mutable, 10); - doEitherLeft.matchTestLeft((l) { - expect(l, "Error"); - }); - - final doEitherRight = Either.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((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( - (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( - (a1, a2) => a1.substring(0, 2) == a2.substring(0, 2)); - final instance2 = Eq.instance( - (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((a1, a2) => a1 == (a2 + 2)); - final instance2 = Eq.instance((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((a1, a2) => a1 == (a2 + 2)); - final instance2 = Eq.instance((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(); - expect(instance.eqv(1, 1), true); - expect(instance.eqv(1, 2), false); - }); - - test('.allEqual', () { - final instance = Eq.allEqual(); - expect(instance.eqv(1, 1), true); - expect(instance.eqv(1, 2), true); - expect(instance.eqv(2, 1), true); - }); - - test('.by', () { - final instance = Eq.instance((a1, a2) => a1 == a2); - final by = Eq.by((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(any.int, any.int).test('curry', (n1, n2) { - expect(subtract(n1, n2), subtract.curry(n1)(n2)); - }); - - Glados2(any.int, any.int).test('curryLast', (n1, n2) { - expect(subtract(n1, n2), subtract.curryLast(n2)(n1)); - }); - - Glados2(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(any.int, any.int, any.int).test('curry', - (n1, n2, n3) { - expect(subtract(n1, n2, n3), subtract.curry(n1)(n2, n3)); - }); - - Glados3(any.int, any.int, any.int).test('curryLast', - (n1, n2, n3) { - expect(subtract(n1, n2, n3), subtract.curryLast(n3)(n1, n2)); - }); - - Glados3(any.int, any.int, any.int).test('curryAll', - (n1, n2, n3) { - expect(subtract(n1, n2, n3), subtract.curryAll(n1)(n2)(n3)); - }); - - Glados3(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(any.int, any.int, any.int).test('curry', - (n1, n2, n3) { - expect(subtract(n1, n2, n3, n1), subtract.curry(n1)(n2, n3, n1)); - }); - - Glados3(any.int, any.int, any.int).test('curryLast', - (n1, n2, n3) { - expect(subtract(n1, n2, n3, n2), subtract.curryLast(n2)(n1, n2, n3)); - }); - - Glados3(any.int, any.int, any.int).test('curryAll', - (n1, n2, n3) { - expect(subtract(n1, n2, n3, n1), subtract.curryAll(n1)(n2)(n3)(n1)); - }); - - Glados3(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(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(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(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(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().test('eqvYear == dateEqYear', (d1, d2) { - expect(d1.eqvYear(d2), Eq.dateEqYear.eqv(d1, d2)); - }); - - Glados2().test('eqvMonth == dateEqMonth', (d1, d2) { - expect(d1.eqvMonth(d2), Eq.dateEqMonth.eqv(d1, d2)); - }); - - Glados2().test('eqvDay == dateEqDay', (d1, d2) { - expect(d1.eqvDay(d2), Eq.dateEqDay.eqv(d1, d2)); - }); - - Glados2().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(Iterable a, Iterable 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( - (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((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()); - expect(ap.getOrElse(() => -1), 1); - }); - - test('None', () { - final List list1 = []; - final ap = list1.head; - expect(ap, isA()); - expect(ap.getOrElse(() => -1), -1); - }); - }); - - group('firstOption', () { - test('Some', () { - final list1 = [1, 2]; - final ap = list1.firstOption; - expect(ap, isA()); - expect(ap.getOrElse(() => -1), 1); - }); - }); - - group('tail', () { - test('Some', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.tail; - expect(ap, isA()); - expect(ap.getOrElse(() => []), [2, 3, 4]); - }); - }); - - group('init', () { - test('Some', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.init; - expect(ap, isA()); - expect(ap.getOrElse(() => []), [1, 2, 3]); - }); - }); - - group('lastOption', () { - test('Some', () { - final list1 = [1, 2, 3, 4]; - final ap = list1.lastOption; - expect(ap, isA()); - 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(0, (b, t) => b - t); - expect(ap, -6); - }); - - test('foldLeftWithIndex', () { - final list1 = [1, 2, 3]; - final ap = list1.foldLeftWithIndex(0, (b, t, i) => b - t - i); - expect(ap, -9); - }); - - test('mapWithIndex', () { - final list1 = [1, 2, 3]; - final ap = list1.mapWithIndex((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()); - }); - - test('none found', () { - expect([1, 2, 3, 4].lookupEq(Eq.eqInt, 5), isA()); - }); - - test('found', () { - var find3 = [1, 2, 3, 4].lookupEq(Eq.eqInt, 3); - expect(find3, isA()); - 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()); - 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((t) => t == 3 ? none() : some(t)); - expect(result, isA()); - }); - }); - - 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( - (a, i) => i == 3 ? none() : some(a + i)); - expect(result, isA()); - }); - }); - - 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((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((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((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((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((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((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( - (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( - (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( - (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( - (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( - (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( - (a) => IOOption( - () { - sideEffect += 1; - return a % 2 == 0 ? some("$a") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, isA()); - expect(sideEffect, list.length); - }); - }); - - group('traverseTaskOptionWithIndex', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionWithIndex( - (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( - (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()); - expect(sideEffect, list.length); - }); - }); - - group('traverseTaskOption', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOption( - (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( - (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()); - expect(sideEffect, list.length); - }); - }); - - group('traverseTaskOptionWithIndex', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionWithIndex( - (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( - (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()); - expect(sideEffect, list.length); - }); - }); - - group('traverseTaskOptionSeq', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionSeq( - (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( - (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()); - expect(sideEffect, 5); - }); - }); - - group('traverseTaskOptionWithIndexSeq', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskOptionWithIndexSeq( - (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( - (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()); - expect(sideEffect, 11); - }); - }); - - group('traverseTaskEither', () { - test('Right', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = list.traverseTaskEither( - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right("$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( - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 - ? right("$a") - : left("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( - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right("$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( - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 - ? right("$a$i") - : left("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( - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return right("$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( - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return a % 2 == 0 - ? right("$a") - : left("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( - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return right("$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( - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return a % 2 == 0 - ? right("$a$i") - : left("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(), some(3), some(4)]; - final result = list.sequenceOption(); - expect(result, isA()); - }); - }); - }); - - 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(1), - left("Error"), - right(3), - right(4) - ]; - final result = list.sequenceEither(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - test('rightsEither', () { - final list = [ - right(1), - right(2), - left('a'), - left('b'), - right(3), - ]; - final result = list.rightsEither(); - expect(result, [1, 2, 3]); - }); - - test('leftsEither', () { - final list = [ - right(1), - right(2), - left('a'), - left('b'), - right(3), - ]; - final result = list.leftsEither(); - expect(result, ['a', 'b']); - }); - - test('partitionEithersEither', () { - final list = [ - right(1), - right(2), - left('a'), - left('b'), - right(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(); - }), - 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()); - 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(); - }), - 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()); - 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(); - }), - 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()); - expect(sideEffect, 3); - }); - }); - }); - - group('FpdartSequenceIterableTaskEither', () { - group('sequenceTaskEither', () { - test('Right', () async { - var sideEffect = 0; - final list = [ - TaskEither(() async { - sideEffect += 1; - return right(1); - }), - TaskEither(() async { - sideEffect += 1; - return right(2); - }), - TaskEither(() async { - sideEffect += 1; - return right(3); - }), - TaskEither(() async { - sideEffect += 1; - return right(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(1); - }), - TaskEither(() async { - sideEffect += 1; - return left("Error"); - }), - TaskEither(() async { - sideEffect += 1; - return right(3); - }), - TaskEither(() async { - sideEffect += 1; - return right(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(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return right(2); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return right(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return right(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(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return left("Error"); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return right(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return right(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(1); - }), - IOEither(() { - sideEffect += 1; - return right(2); - }), - IOEither(() { - sideEffect += 1; - return right(3); - }), - IOEither(() { - sideEffect += 1; - return right(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(1); - }), - IOEither(() { - sideEffect += 1; - return left("Error"); - }), - IOEither(() { - sideEffect += 1; - return right(3); - }), - IOEither(() { - sideEffect += 1; - return right(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()); - }); - }); - }); - - 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()); - }); - }); - }); - - 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(), - ); - }); - }); - }); - - 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()); - }); - }); - }); - - 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()); - }); - }); - }); - - group('extract', () { - test('valid', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - value.extract('b').matchTestSome((t) { - expect(t, 2); - }); - }); - }); - - test('wrong type', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect(value.extract('b'), isA()); - }); - }); - }); - - group('extractMap', () { - test('no map', () { - testImmutableMap({'a': 1}, (value) { - expect(value.extractMap('a'), isA()); - }); - }); - - 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('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(), - ); - }); - }); - }); - - 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()); - }); - }); - }); - - 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()); - - final result = value.deleteAt(Eq.instance((a1, a2) => a1 == a2), 'b'); - expect(value.lookup('b'), isA()); - expect(result.lookup('b'), isA()); - }); - }); - - group('upsertAt', () { - test('insert', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect(value.lookup('e'), isA()); - - final result = - value.upsertAt(Eq.instance((a1, a2) => a1 == a2), 'e', 10); - expect(value.lookup('e'), isA()); - - 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({}, (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()); - - result.matchTestSome((t) { - expect(t.$1, 2); - expect(t.$2.lookup('b'), isA()); - }); - }); - }); - - test('None', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.pop(Eq.instance((a1, a2) => a1 == a2), 'e'), - isA(), - ); - }); - }); - }); - - test('foldLeft', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldLeft(Order.allEqual(), '', (acc, a) => '$acc$a'), - '1234', - ); - }); - }); - - test('foldLeftWithIndex', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldLeftWithIndex( - Order.allEqual(), '', (acc, a, i) => '$acc$a$i'), - '10213243', - ); - }); - }); - - test('foldLeftWithKey', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldLeftWithKey( - Order.allEqual(), '', (acc, k, v) => '$acc$k$v'), - 'a1b2c3d4', - ); - }); - }); - - test('foldLeftWithKeyAndIndex', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldLeftWithKeyAndIndex( - 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(Order.allEqual(), '', (a, acc) => '$acc$a'), - '4321', - ); - }); - }); - - test('foldRightWithIndex', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldRightWithIndex( - Order.allEqual(), '', (a, acc, i) => '$acc$a$i'), - '40312213', - ); - }); - }); - - test('foldRightWithKey', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldRightWithKey( - Order.allEqual(), '', (k, v, acc) => '$acc$k$v'), - 'd4c3b2a1', - ); - }); - }); - - test('foldRightWithKeyAndIndex', () { - testImmutableMap({'a': 1, 'b': 2, 'c': 3, 'd': 4}, (value) { - expect( - value.foldRightWithKeyAndIndex( - 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( - 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.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.none(); - final value = option.getOrElse(() => 0); - expect(value, 0); - }); - }); - - test('elem', () { - final m1 = Option.of(10); - final m2 = Option.none(); - final eq = Eq.instance((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(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(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((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()); - }); - - test('"10,0"', () { - final result = "10,0".toNumOption; - expect(result, isA()); - }); - - test('"10a"', () { - final result = "10a".toNumOption; - expect(result, isA()); - }); - - test('"none"', () { - final result = "none".toNumOption; - expect(result, isA()); - }); - - test('"10.512a"', () { - final result = "10.512a".toNumOption; - expect(result, isA()); - }); - - test('"1f"', () { - final result = "1f".toNumOption; - expect(result, isA()); - }); - }); - }); - - 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()); - }); - - test('"-1.e3"', () { - final result = "-1.e3".toIntOption; - expect(result, isA()); - }); - - test('"1234E+7"', () { - final result = "1234E+7".toIntOption; - expect(result, isA()); - }); - - test('"0."', () { - final result = "0.".toIntOption; - expect(result, isA()); - }); - - test('"10.5"', () { - final result = "10.5".toIntOption; - expect(result, isA()); - }); - - test('"10.0"', () { - final result = "10.0".toIntOption; - expect(result, isA()); - }); - - test('"10.512"', () { - final result = "10.512".toIntOption; - expect(result, isA()); - }); - - test('" 3.14 \xA0"', () { - final result = " 3.14 \xA0".toIntOption; - expect(result, isA()); - }); - - test('".0"', () { - final result = ".0".toIntOption; - expect(result, isA()); - }); - - test('"10,0"', () { - final result = "10,0".toIntOption; - expect(result, isA()); - }); - - test('"10a"', () { - final result = "10a".toIntOption; - expect(result, isA()); - }); - - test('"none"', () { - final result = "none".toIntOption; - expect(result, isA()); - }); - - test('"10.512a"', () { - final result = "10.512a".toIntOption; - expect(result, isA()); - }); - - test('"1f"', () { - final result = "1f".toIntOption; - expect(result, isA()); - }); - }); - }); - - 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()); - }); - - test('"- 10"', () { - final result = "- 10".toDoubleOption; - expect(result, isA()); - }); - - test('"10,0"', () { - final result = "10,0".toDoubleOption; - expect(result, isA()); - }); - - test('"10a"', () { - final result = "10a".toDoubleOption; - expect(result, isA()); - }); - - test('"none"', () { - final result = "none".toDoubleOption; - expect(result, isA()); - }); - - test('"10.512a"', () { - final result = "10.512a".toDoubleOption; - expect(result, isA()); - }); - - test('"1f"', () { - final result = "1f".toDoubleOption; - expect(result, isA()); - }); - }); - }); - - 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()); - }); - - test('"FALSE"', () { - final result = "FALSE".toBoolOption; - expect(result, isA()); - }); - - test('"NO"', () { - final result = "NO".toBoolOption; - expect(result, isA()); - }); - - test('"YES"', () { - final result = "YES".toBoolOption; - expect(result, isA()); - }); - - test('"0"', () { - final result = "0".toBoolOption; - expect(result, isA()); - }); - - test('"1"', () { - final result = "1".toBoolOption; - expect(result, isA()); - }); - }); - }); - - 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(0, (a1, a2) => a1 + a2, (a) => -a); - - test('Semigroup', () { - expect(instance, isA()); - }); - - test('Monoid', () { - expect(instance, isA()); - }); - }); - - test('inverse', () { - final instance = Group.instance(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(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(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(); - - test('Eq', () { - expect(instance, isA()); - }); - }); - - test('.fromUniversalHashCode', () { - const source1 = 1; - const source2 = 1; - final instance = Hash.fromUniversalHashCode(); - 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.tryCatch(() => 10, (_, __) => 'error'); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Failure', () { - final task = IOEither.tryCatch(() { - throw UnimplementedError(); - }, (_, __) => 'error'); - final r = task.run(); - r.match((l) => expect(l, 'error'), (_) { - fail('should be left'); - }); - }); - - test('throws Exception', () { - final task = IOEither.tryCatch(() { - throw UnimplementedError(); - }, (error, _) { - expect(error, isA()); - return 'error'; - }); - final r = task.run(); - r.match((l) => expect(l, 'error'), (_) { - fail('should be left'); - }); - }); - }); - - group('flatMap', () { - test('Right', () { - final task = IOEither(() => Either.of(10)); - final ap = - task.flatMap((r) => IOEither(() => Either.of(r + 10))); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 20)); - }); - - test('Left', () { - final task = IOEither(() => Either.left('abc')); - final ap = - task.flatMap((r) => IOEither(() => 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(() => 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(() => 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(() => Either.of(10)); - final ap = - task.flatMapTask((r) => TaskEither.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(() => Either.left('abc')); - final ap = - task.flatMapTask((r) => TaskEither.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(() => Either.of(10)); - final ap = task.ap(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(() => Either.left('abc')); - final ap = task.ap(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(() => 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(() => 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(() => 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(() => 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(() => 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(() => 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(() => Either.of(10)); - final ap = task.map2( - IOEither(() => 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(() => Either.left('abc')); - final ap = task.map2( - IOEither(() => 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(() => Either.of(10)); - final ap = task.map3( - IOEither(() => Either.of(2)), - IOEither(() => 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(() => Either.left('abc')); - final ap = task.map3( - IOEither(() => Either.of(2)), - IOEither(() => 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(() => Either.of(10)); - final ap = - task.andThen(() => IOEither(() => Either.of(12.5))); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 12.5)); - }); - - test('Left', () { - final task = IOEither(() => Either.left('abc')); - final ap = - task.andThen(() => IOEither(() => 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(() => Either.of(10)); - final ap = task(IOEither(() => Either.of(12.5))); - final r = ap.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 12.5)); - }); - - test('Left', () { - final task = IOEither(() => Either.left('abc')); - final ap = task(IOEither(() => 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(() => 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(() => 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(() => 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(() => 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(() => Either.of(10)); - final func = task.run(); - expect(func, isA>()); - final r = func; - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - group('fromEither', () { - test('Right', () { - final task = IOEither.fromEither(Either.of(10)); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final task = IOEither.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.fromOption(Option.of(10), () => 'none'); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('None', () { - final task = - IOEither.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.fromNullable(10, () => "Error"); - final result = task.run(); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () { - final task = IOEither.fromNullable(null, () => "Error"); - final result = task.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('fromPredicate', () { - test('True', () { - final task = - IOEither.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.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.fromIO(IO(() => 10)); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('left', () { - final task = IOEither.left('none'); - final r = task.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('right', () { - final task = IOEither.right(10); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('leftTask', () { - final task = IOEither.leftIO(IO(() => 'none')); - final r = task.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('rightTask', () { - final task = IOEither.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(() => 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(() => 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(() => Either.of(10)); - final ex = task.getOrElse((l) => l.length); - final r = ex.run(); - expect(r, 10); - }); - - test('Left', () { - final task = IOEither(() => Either.left('none')); - final ex = task.getOrElse((l) => l.length); - final r = ex.run(); - expect(r, 4); - }); - }); - - group('orElse', () { - test('Right', () { - final task = IOEither(() => Either.of(10)); - final ex = task.orElse((l) => IOEither(() => Right(l.length))); - final r = ex.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('Left', () { - final task = IOEither(() => Either.left('none')); - final ex = task.orElse((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(() => 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(() => 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(() => 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.of(10); - final r = task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('flatten', () { - final task = IOEither>.of( - IOEither.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.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(1); - }), - IOEither(() { - sideEffect += 1; - return right(2); - }), - IOEither(() { - sideEffect += 1; - return right(3); - }), - IOEither(() { - sideEffect += 1; - return right(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(1); - }), - IOEither(() { - sideEffect += 1; - return left("Error"); - }), - IOEither(() { - sideEffect += 1; - return right(3); - }), - IOEither(() { - sideEffect += 1; - return right(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(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(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(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(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.Do((_) => _(IOEither.of(10))); - final run = doIOEither.run(); - run.matchTestRight((t) { - expect(t, 10); - }); - }); - - test('should extract the correct values', () { - final doIOEither = IOEither.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.Do((_) { - final a = _(IOEither.of(10)); - final b = _(IOEither.of(5)); - final c = _(IOEither.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.Do((_) { - _(IOEither.of(10)); - throw UnimplementedError(); - }); - - expect(doIOEither.run, throwsA(const TypeMatcher())); - }); - - test('should rethrow if Left is thrown inside Do', () { - final doIOEither = IOEither.Do((_) { - _(IOEither.of(10)); - throw Left('Error'); - }); - - expect(doIOEither.run, throwsA(const TypeMatcher())); - }); - - test('should no execute past the first Left', () { - var mutable = 10; - final doIOEitherLeft = IOEither.Do((_) { - final a = _(IOEither.of(10)); - final b = _(IOEither.left("Error")); - mutable += 10; - return a + b; - }); - - final runLeft = doIOEitherLeft.run(); - expect(mutable, 10); - runLeft.matchTestLeft((l) { - expect(l, "Error"); - }); - - final doIOEitherRight = IOEither.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.tryCatch(() => 10); - final r = io.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('throws Exception', () { - final io = IOOption.tryCatch(() { - throw UnimplementedError(); - }); - final r = io.run(); - expect(r, isA()); - }); - }); - - group('tryCatchK', () { - test('Success', () { - final io = IOOption.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.of(10); - final ap = io.flatMap(IOOption.tryCatchK((_) { - throw UnimplementedError(); - })); - final r = ap.run(); - expect(r, isA()); - }); - }); - - group('flatMap', () { - test('Some', () { - final io = IOOption(() => Option.of(10)); - final ap = io.flatMap((r) => IOOption(() => Option.of(r + 10))); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 20)); - }); - - test('None', () { - final io = IOOption(() => Option.none()); - final ap = io.flatMap((r) => IOOption(() => Option.of(r + 10))); - final r = ap.run(); - expect(r, isA()); - }); - }); - - group('ap', () { - test('Some', () { - final io = IOOption(() => Option.of(10)); - final ap = io.ap(IOOption(() => Option.of((int c) => c / 2))); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 5.0)); - }); - - test('None', () { - final io = IOOption(() => Option.none()); - final ap = io.ap(IOOption(() => Option.of((int c) => c / 2))); - final r = ap.run(); - expect(r, isA()); - }); - }); - - group('map', () { - test('Some', () { - final io = IOOption(() => 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(() => Option.none()); - final ap = io.map((r) => r / 2); - final r = ap.run(); - expect(r, isA()); - }); - }); - - group('map2', () { - test('Some', () { - final io = IOOption(() => Option.of(10)); - final ap = io.map2( - IOOption(() => Option.of(2)), (b, c) => b / c); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 5.0)); - }); - - test('None', () { - final io = IOOption(() => Option.none()); - final ap = io.map2( - IOOption(() => Option.of(2)), (b, c) => b / c); - final r = ap.run(); - expect(r, isA()); - }); - }); - - group('map3', () { - test('Some', () { - final io = IOOption(() => Option.of(10)); - final ap = io.map3(IOOption(() => Option.of(2)), - IOOption(() => 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(() => Option.none()); - final ap = io.map3(IOOption(() => Option.of(2)), - IOOption(() => Option.of(5)), (b, c, d) => b * c / d); - final r = ap.run(); - expect(r, isA()); - }); - }); - - group('andThen', () { - test('Some', () { - final io = IOOption(() => Option.of(10)); - final ap = io.andThen(() => IOOption(() => Option.of(12.5))); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 12.5)); - }); - - test('None', () { - final io = IOOption(() => Option.none()); - final ap = io.andThen(() => IOOption(() => Option.of(12.5))); - final r = ap.run(); - expect(r, isA()); - }); - }); - - group('call', () { - test('Some', () { - final io = IOOption(() => Option.of(10)); - final ap = io(IOOption(() => Option.of(12.5))); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 12.5)); - }); - - test('None', () { - final io = IOOption(() => Option.none()); - final ap = io(IOOption(() => Option.of(12.5))); - final r = ap.run(); - expect(r, isA()); - }); - }); - - test('pure', () { - final io = IOOption(() => Option.none()); - final ap = io.pure('abc'); - final r = ap.run(); - r.matchTestSome((r) => expect(r, 'abc')); - }); - - test('run', () { - final io = IOOption(() => Option.of(10)); - final r = io.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - group('fromEither', () { - test('Some', () { - final io = IOOption.fromEither(Either.of(10)); - final r = io.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('None', () { - final io = IOOption.fromEither(Either.left('none')); - final r = io.run(); - expect(r, isA()); - }); - }); - - group('fromNullable', () { - test('Right', () { - final io = IOOption.fromNullable(10); - final result = io.run(); - result.matchTestSome((r) { - expect(r, 10); - }); - }); - - test('Left', () { - final io = IOOption.fromNullable(null); - final result = io.run(); - expect(result, isA()); - }); - }); - - group('fromPredicate', () { - test('True', () { - final io = IOOption.fromPredicate(20, (n) => n > 10); - final r = io.run(); - r.matchTestSome((r) => expect(r, 20)); - }); - - test('False', () { - final io = IOOption.fromPredicate(10, (n) => n > 10); - final r = io.run(); - expect(r, isA()); - }); - }); - - test('none()', () { - final io = IOOption.none(); - final r = io.run(); - expect(r, isA()); - }); - - test('some()', () { - final io = IOOption.some(10); - final r = io.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - group('match', () { - test('Some', () { - final io = IOOption(() => Option.of(10)); - final ex = io.match(() => -1, (r) => r + 10); - final r = ex.run(); - expect(r, 20); - }); - - test('None', () { - final io = IOOption(() => Option.none()); - final ex = io.match(() => -1, (r) => r + 10); - final r = ex.run(); - expect(r, -1); - }); - }); - - group('getOrElse', () { - test('Some', () { - final io = IOOption(() => Option.of(10)); - final ex = io.getOrElse(() => -1); - final r = ex.run(); - expect(r, 10); - }); - - test('None', () { - final io = IOOption(() => Option.none()); - final ex = io.getOrElse(() => -1); - final r = ex.run(); - expect(r, -1); - }); - }); - - group('orElse', () { - test('Some', () { - final io = IOOption(() => Option.of(10)); - final ex = io.orElse(() => IOOption(() => Option.of(-1))); - final r = ex.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('None', () { - final io = IOOption(() => Option.none()); - final ex = io.orElse(() => IOOption(() => Option.of(-1))); - final r = ex.run(); - r.matchTestSome((r) => expect(r, -1)); - }); - }); - - group('alt', () { - test('Some', () { - final io = IOOption(() => 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(() => 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.of(10); - final r = io.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('flatten', () { - final io = IOOption>.of(IOOption.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()); - }); - }); - - 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(); - }), - 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()); - expect(sideEffect, list.length); - }); - }); - - group('traverseList', () { - test('Some', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = IOOption.traverseList( - 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( - list, - (a) => IOOption( - () { - sideEffect += 1; - return a % 2 == 0 ? some("$a") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, isA()); - expect(sideEffect, list.length); - }); - }); - - group('traverseListWithIndex', () { - test('Some', () { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = IOOption.traverseListWithIndex( - 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( - list, - (a, i) => IOOption( - () { - sideEffect += 1; - return a % 2 == 0 ? some("$a$i") : none(); - }, - ), - ); - expect(sideEffect, 0); - final result = traverse.run(); - expect(result, isA()); - expect(sideEffect, list.length); - }); - }); - - group('Do Notation', () { - test('should return the correct value', () { - final doIOOption = IOOption.Do((_) => _(IOOption.of(10))); - final run = doIOOption.run(); - run.matchTestSome((t) { - expect(t, 10); - }); - }); - - test('should extract the correct values', () { - final doIOOption = IOOption.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.Do((_) { - final a = _(IOOption.of(10)); - final b = _(IOOption.of(5)); - final c = _(IOOption.none()); - return a + b + c; - }); - final run = doIOOption.run(); - expect(run, isA()); - }); - - test('should rethrow if throw is used inside Do', () { - final doIOOption = IOOption.Do((_) { - _(IOOption.of(10)); - throw UnimplementedError(); - }); - - expect( - doIOOption.run, throwsA(const TypeMatcher())); - }); - - test('should rethrow if None is thrown inside Do', () { - final doIOOption = IOOption.Do((_) { - _(IOOption.of(10)); - throw const None(); - }); - - expect(doIOOption.run, throwsA(const TypeMatcher())); - }); - - test('should no execute past the first Left', () { - var mutable = 10; - final doIOOptionNone = IOOption.Do((_) { - final a = _(IOOption.of(10)); - final b = _(IOOption.none()); - mutable += 10; - return a + b; - }); - - final runNone = doIOOptionNone.run(); - expect(mutable, 10); - expect(runNone, isA()); - - final doIOOptionSome = IOOption.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()); - }); - - test('Applicative', () { - expect(io, isA()); - }); - - test('Functor', () { - expect(io, isA()); - }); - }); - - 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(); - 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(); - 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(IO(() => 'abc'), (a, c) => a + c.length); - final r = ap.run(); - expect(r, 13); - }); - - test('map3', () { - final io = IO(() => 10); - final ap = io.map3( - 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>()); - final r = ap.run(); - expect(r, 10); - }); - - test('run', () { - final io = IO(() => 10); - final r = io.run(); - expect(r, isA()); - 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(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(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(0, (a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA()); - }); - }); - - test('.instance (int)', () { - final instance = Monoid.instance(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('', (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(0, (a1, a2) => a1 + a2); - final eq = Eq.instance((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(0, (a1, a2) => a1 + a2); - expect(instance.combineN(0, 10), 0); - expect(instance.combineN(1, 10), 10); - }); - - test('.reverse', () { - final instance = Monoid.instance('', (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>()); - expect(castString, isA>()); - }); - }); - - 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.of); - result.matchTestSome((t) { - expect(t, input); - }); - }); - }); - }); - - group('is a', () { - final option = Option.of(10); - - test('Monad', () { - expect(option, isA()); - }); - - test('Applicative', () { - expect(option, isA()); - }); - - test('Functor', () { - expect(option, isA()); - }); - - test('Extend', () { - expect(option, isA()); - }); - - test('Filterable', () { - expect(option, isA()); - }); - }); - - 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(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( - 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.none(); - final pure = option.ap(Option.of((int i) => i + 1)); - expect(pure, isA()); - }); - }); - - group('flatMap', () { - test('Some', () { - final option = Option.of(10); - final flatMap = option.flatMap((a) => Option.of(a + 1)); - flatMap.matchTestSome((some) => expect(some, 11)); - }); - - test('None', () { - final option = Option.none(); - final flatMap = option.flatMap((a) => Option.of(a + 1)); - expect(flatMap, isA()); - }); - }); - - 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((a) => null); - expect(flatMap, isA()); - }); - }); - - 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((a) => throw "fail"); - expect(flatMap, isA()); - }); - }); - - 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.none(); - final value = option.extend((t) => t.isSome() ? 'valid' : 'invalid'); - expect(value, isA()); - }); - }); - - group('duplicate', () { - test('Some', () { - final option = Option.of(10); - final value = option.duplicate(); - value.matchTestSome((some) => expect(some, isA())); - }); - - test('None', () { - final option = Option.none(); - final value = option.duplicate(); - expect(value, isA()); - }); - }); - - 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()); - }); - - test('None', () { - final option = Option.none(); - final value = option.filter((a) => a > 5); - expect(value, isA()); - }); - }); - - group('filterMap', () { - test('Some', () { - final option = Option.of(10); - final value = option.filterMap((a) => Option.of('$a')); - value.matchTestSome((some) => expect(some, '10')); - }); - - test('None', () { - final option = Option.none(); - final value = option.filterMap((a) => Option.of('$a')); - expect(value, isA()); - }); - }); - - group('partition', () { - test('Some (true)', () { - final option = Option.of(10); - final value = option.partition((a) => a > 5); - expect(value.$1, isA()); - 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()); - }); - - test('None', () { - final option = Option.none(); - final value = option.partition((a) => a > 5); - expect(value.$1, isA()); - expect(value.$2, isA()); - }); - }); - - group('partitionMap', () { - test('Some (right)', () { - final option = Option.of(10); - final value = - option.partitionMap((a) => Either.of(a / 2)); - expect(value.$1, isA()); - value.$2.matchTestSome((some) => expect(some, 5.0)); - }); - - test('Some (left)', () { - final option = Option.of(10); - final value = - option.partitionMap((a) => Either.left('$a')); - value.$1.matchTestSome((some) => expect(some, '10')); - expect(value.$2, isA()); - }); - - test('None', () { - final option = Option.none(); - final value = - option.partitionMap((a) => Either.of(a / 2)); - expect(value.$1, isA()); - expect(value.$2, isA()); - }); - }); - - group('fromEither', () { - test('Right', () { - final option = Option.fromEither(Either.of(10)); - option.matchTestSome((some) => expect(some, 10)); - }); - - test('Left', () { - final option = Option.fromEither(Either.left('none')); - expect(option, isA()); - }); - }); - - group('fromJson', () { - test('int', () { - final option = Option.fromJson(10, (a) => a as int); - option.matchTestSome((some) => expect(some, 10)); - }); - - test('DateTime', () { - final now = DateTime.now(); - final option = Option.fromJson( - now.toIso8601String(), (a) => DateTime.parse(a as String)); - option.matchTestSome((some) => expect(some, now)); - }); - - test('DateTime failure', () { - final option = Option.fromJson( - "fail", (a) => DateTime.parse(a as String)); - expect(option, isA()); - }); - - test('null', () { - final option = Option.fromJson(null, (a) => a as int); - expect(option, isA()); - }); - }); - - group('fromPredicate', () { - test('Some', () { - final option = Option.fromPredicate(10, (a) => a > 5); - option.matchTestSome((some) => expect(some, 10)); - }); - - test('None', () { - final option = Option.fromPredicate(10, (a) => a < 5); - expect(option, isA()); - }); - }); - - group('fromPredicateMap', () { - test('Some', () { - final option = - Option.fromPredicateMap(10, (a) => a > 5, (a) => '$a'); - option.matchTestSome((some) => expect(some, '10')); - }); - - test('None', () { - final option = - Option.fromPredicateMap(10, (a) => a < 5, (a) => '$a'); - expect(option, isA()); - }); - }); - - 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.none())); - expect(option, isA()); - }); - }); - - group('separate', () { - test('Right', () { - final option = Option.separate(Option.of(Either.of(10))); - expect(option.$1, isA()); - option.$2.matchTestSome((some) => expect(some, 10)); - }); - - test('Left', () { - final option = - Option.separate(Option.of(Either.left('none'))); - option.$1.matchTestSome((some) => expect(some, 'none')); - expect(option.$2, isA()); - }); - }); - - test('None', () { - final option = Option.none(); - expect(option, isA()); - }); - - 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.none(); - expect(option.isNone(), true); - expect(option.isSome(), false); - }); - - test('getEq', () { - final eq = Option.getEq(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.none()), false); - expect(eq.eqv(Option.none(), Option.none()), true); - }); - - test('getOrder', () { - final order = - Option.getOrder(Order.from((a1, a2) => a1.compareTo(a2))); - final option1 = Option.of(10); - final option2 = Option.of(9); - final option3 = Option.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.fromNullable(10); - final m2 = Option.fromNullable(null); - expect(m1, isA()); - expect(m2, isA()); - }); - - test('tryCatch', () { - final m1 = Option.tryCatch(() => 10); - final m2 = Option.tryCatch(() => throw UnimplementedError()); - expect(m1, isA()); - expect(m2, isA()); - }); - - test('toEither', () { - final m1 = Option.of(10); - final m2 = Option.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.none(); - expect(m1.toNullable(), 10); - expect(m1.toNullable(), isA()); - expect(m2.toNullable(), null); - }); - - test('pure', () { - final m1 = Option.of(10); - final m2 = Option.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.none(); - m1 - .andThen(() => Option.of('abc')) - .matchTestSome((some) => expect(some, 'abc')); - expect(m2.andThen(() => Option.of('abc')), isA()); - }); - - test('call', () { - final m1 = Option.of(10); - final m2 = Option.none(); - m1(Option.of('abc')).matchTestSome((some) => expect(some, 'abc')); - expect(m2(Option.of('abc')), isA()); - }); - - test('match', () { - final m1 = Option.of(10); - final m2 = Option.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.none(); - expect(m1.fold(() => 'none', (some) => 'some'), 'some'); - expect(m2.fold(() => 'none', (some) => 'some'), 'none'); - }); - - test('none()', () { - final m = Option.none(); - expect(m, isA()); - }); - - test('of()', () { - final m = Option.of(10); - expect(m, isA>()); - }); - - test('getFirstMonoid', () { - final m = Option.getFirstMonoid(); - expect(m.empty, isA()); - 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(); - expect(m.empty, isA()); - 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(Semigroup.instance((a1, a2) => a1 + a2)); - expect(m.empty, isA()); - m - .combine(Option.of(10), Option.of(20)) - .matchTestSome((some) => expect(some, 30)); - expect(m.combine(Option.of(10), Option.none()), isA()); - expect(m.combine(Option.none(), Option.of(10)), isA()); - }); - - group('toString', () { - test('Some', () { - final m = Option.of(10); - expect(m.toString(), 'Some(10)'); - }); - - test('None', () { - final m = Option.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(), some(3), some(4)]; - final result = Option.sequenceList(list); - expect(result, isA()); - }); - }); - - group('traverseList', () { - test('Some', () { - final list = [1, 2, 3, 4, 5, 6]; - final result = - Option.traverseList(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( - list, - (a) => a % 2 == 0 ? some("$a") : none(), - ); - expect(result, isA()); - }); - }); - - group('traverseListWithIndex', () { - test('Some', () { - final list = [1, 2, 3, 4, 5, 6]; - final result = Option.traverseListWithIndex( - 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( - list, - (a, i) => i % 2 == 0 ? some("$a$i") : none(), - ); - expect(result, isA()); - }); - }); - - group('safeCast', () { - test('dynamic', () { - final castInt = Option.safeCast(10); - final castString = Option.safeCast('abc'); - expect(castInt, isA>()); - expect(castString, isA>()); - }); - - test('Some', () { - final cast = Option.safeCast(10); - cast.matchTestSome((r) { - expect(r, 10); - }); - }); - - test('None', () { - final cast = Option.safeCast('abc'); - expect(cast, isA()); - }); - }); - - 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.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.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.none(); - final m4 = Option.of(10); - final map1 = {'m1': m1, 'm2': m4}; - final map2 = {'m1': m1, 'm2': m2}; - final map3 = {'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.none(); - final m4 = Option.none(); - final m5 = Option.none(); - final map1 = {'m1': m3, 'm2': m3}; - final map2 = {'m1': m3, 'm2': m4}; - final map3 = {'m1': m3, 'm2': m5}; - final map4 = {'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(10); - cast.matchTestSome((r) { - expect(r, 10); - }); - }); - - test('None', () { - final cast = Option.safeCastStrict('abc'); - expect(cast, isA()); - }); - }); - - group('Do Notation', () { - test('should traverse over a list', () async { - const testOption = const Option>.of( - ['/', '/test', null], - ); - - Option> doNotation = Option.Do( - ($) { - List optionList = $(testOption); - return $(optionList.traverseOption( - (stringValue) => optionOf(stringValue).flatMap( - (uri) => optionOf( - Uri.tryParse(uri), - ), - ), - )); - }, - ); - - expect(doNotation, equals(const Option.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.none()); - return a + b + c; - }); - - expect(doOption, isA()); - }); - - test('should rethrow if throw is used inside Do', () { - final doOption = () => Option.Do((_) { - _(Option.of(10)); - throw UnimplementedError(); - }); - - expect(doOption, throwsA(const TypeMatcher())); - }); - - test('should rethrow if None is thrown inside Do', () { - final doOption = () => Option.Do((_) { - _(Option.of(10)); - throw None(); - }); - - expect(doOption, throwsA(const TypeMatcher())); - }); - - test('should throw if the error is not None', () { - final doOption = () => Option.Do((_) { - _(Option.of(10)); - throw UnimplementedError(); - }); - - expect(doOption, throwsA(const TypeMatcher())); - }); - - test('should no execute past the first None', () { - var mutable = 10; - final doOptionNone = Option.Do((_) { - final a = _(Option.of(10)); - final b = _(Option.none()); - mutable += 10; - return a + b; - }); - - expect(mutable, 10); - expect(doOptionNone, isA()); - - 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((a1, a2) => a1.compareTo(a2)); - - test('Eq', () { - expect(instance, isA()); - }); - - test('PartialOrder', () { - expect(instance, isA()); - }); - }); - - test('.from', () { - final instance = Order.from((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((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(); - expect(instance.compare(1, 1), 0); - expect(instance.compare(1, 2), 0); - expect(instance.compare(2, 1), 0); - }); - - test('.whenEqual', () { - final instance1 = - Order.fromLessThan((a1, a2) => a1.length < a2.length); - final instance2 = Order.from( - (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((a1, a2) => a1.compareTo(a2)); - final by = Order.by((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((a1, a2) => a1.compareTo(a2)); - expect(instance.min(0, 10), 0); - expect(instance.min(10, 0), 0); - }); - - test('max', () { - final instance = Order.from((a1, a2) => a1.compareTo(a2)); - expect(instance.max(0, 10), 10); - expect(instance.max(10, 0), 10); - }); - - test('eqv', () { - final instance = Order.from((a1, a2) => a1.compareTo(a2)); - expect(instance.eqv(0, 10), false); - expect(instance.eqv(0, 0), true); - }); - - test('neqv', () { - final instance = Order.from((a1, a2) => a1.compareTo(a2)); - expect(instance.neqv(0, 10), true); - expect(instance.neqv(0, 0), false); - }); - - test('lteqv', () { - final instance = Order.from((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((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((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((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()); - }); - }); - }); -} 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(); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 12.2); - }); - }); - - test('asks', () async { - final apply = ReaderTaskEither.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( - (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( - (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( - (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( - (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.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.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.tryCatch((_) { - throw UnimplementedError(); - }, (error, _) { - expect(error, isA()); - return 'error'; - }); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "error"); - }); - }); - }); - - group('flatMap', () { - test('Right', () async { - final apply = ReaderTaskEither( - (env) async => Either.of(env.toInt()), - ).flatMap( - (r) => ReaderTaskEither( - (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( - (env) async => Either.left("$env"), - ).flatMap( - (r) => ReaderTaskEither( - (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( - (env) async => Either.of(env.toInt()), - ).ap( - 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( - (env) async => Either.left("$env"), - ).ap( - 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( - (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( - (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( - (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( - (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( - (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( - (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( - (env) async => Either.of(env.toInt()), - ).map2( - 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( - (env) async => Either.left('$env'), - ).map2( - 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( - (env) async => Either.of(env.toInt()), - ).map3( - 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( - (env) async => Either.left('$env'), - ).map3( - 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( - (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( - (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( - (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( - (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( - (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( - (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( - (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( - (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( - (env) async => Either.of(env.toInt()), - ); - - final future = apply.run(12.2); - expect(future, isA()); - final result = await future; - result.matchTestRight((r) { - expect(r, 12); - }); - }); - - group('fromEither', () { - test('Right', () async { - final apply = ReaderTaskEither.fromEither( - Either.of(10), - ); - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither.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.fromOption( - Option.of(10), - () => 'none', - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither.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.fromNullable( - 10, - () => "Error", - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither.fromNullable( - null, - () => "error", - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "error"); - }); - }); - }); - - group('fromNullableAsync', () { - test('Right', () async { - final apply = ReaderTaskEither.fromNullableAsync( - 10, - Task( - () async => "Error", - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither.fromNullableAsync( - null, - Task( - () async => "error", - ), - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "error"); - }); - }); - }); - - test('fromTask', () async { - final apply = ReaderTaskEither.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.fromIOOption( - IOOption.of(10), - () => "none", - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither.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.fromTaskOption( - TaskOption.of(10), - () => "none", - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final apply = ReaderTaskEither.fromTaskOption( - TaskOption.none(), - () => "none", - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "none"); - }); - }); - }); - - test('fromTaskEither', () async { - final apply = ReaderTaskEither.fromTaskEither( - TaskEither.of(10), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('fromIO', () async { - final apply = ReaderTaskEither.fromIO( - IO( - () => 10, - ), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('fromIOEither', () async { - final apply = ReaderTaskEither.fromIOEither( - IOEither.of(10), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('fromReader', () async { - final apply = ReaderTaskEither.fromReader( - Reader((env) => env.toInt()), - ); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 12); - }); - }); - - test('leftReader', () async { - final apply = ReaderTaskEither.leftReader( - Reader((env) => "$env"), - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, "12.2"); - }); - }); - - test('left', () async { - final apply = ReaderTaskEither.left( - 'none', - ); - - final result = await apply.run(12.2); - result.matchTestLeft((l) { - expect(l, 'none'); - }); - }); - - test('leftTask', () async { - final apply = ReaderTaskEither.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( - (env) async => Either.of(env.toInt()), - ).orElse( - (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( - (env) async => Either.left('$env'), - ).orElse( - (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( - (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( - (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( - (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.of(10); - - final result = await apply.run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('flatten', () async { - final apply = ReaderTaskEither>.of( - ReaderTaskEither.of(10), - ); - - final result = await ReaderTaskEither.flatten(apply).run(12.2); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('chainFirst', () async { - final apply = ReaderTaskEither.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.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.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.Do((_) async { - final a = await _(ReaderTaskEither.of(10)); - final b = await _(ReaderTaskEither.asks((env) => env.toInt())); - final c = await _( - ReaderTaskEither.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.Do((_) { - _(ReaderTaskEither.of(10)); - throw UnimplementedError(); - }); - - expect( - () => doTaskEither.run(12.2), - throwsA( - const TypeMatcher(), - ), - ); - }); - - test('should rethrow if Left is thrown inside Do', () { - final doTaskEither = ReaderTaskEither.Do((_) { - _( - ReaderTaskEither.of(10), - ); - throw Left('error'); - }); - - expect( - () => doTaskEither.run(12.2), - throwsA( - const TypeMatcher(), - ), - ); - }); - - test('should no execute past the first Left', () async { - var mutable = 10; - - final doTaskEitherLeft = - ReaderTaskEither.Do((_) async { - final a = await _(ReaderTaskEither.of(10)); - final b = - await _(ReaderTaskEither.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.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(); - - final result = await apply.run("abc"); - expect(result, "abc"); - }); - - test('asks', () async { - final apply = ReaderTask.asks( - (env) => env.length, - ); - - final result = await apply.run("abc"); - expect(result, 3); - }); - - group('map', () { - test('int to int', () async { - final apply = ReaderTask( - (env) async => env.length, - ).map((a) => a + 1); - - final result = await apply.run("abc"); - expect(result, 4); - }); - }); - - test('ap', () async { - final apply = ReaderTask( - (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( - (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( - (env) async => env.length, - ).pure('abc'); - - final result = await apply.run("abc"); - expect(result, 'abc'); - }); - - test('map2', () async { - final apply = ReaderTask( - (env) async => env.length, - ).map2( - ReaderTask.of('abc'), - (a, c) => a + c.length, - ); - - final result = await apply.run("abc"); - expect(result, 6); - }); - - test('map3', () async { - final apply = ReaderTask( - (env) async => env.length, - ).map3( - 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.of(10); - - final result = await apply.run("abc"); - expect(result, 10); - }); - - test('run', () async { - final apply = ReaderTask.of(10); - final future = apply.run("abc"); - - expect(future, isA>()); - final result = await future; - expect(result, 10); - }); - - test('flatten', () async { - final apply = ReaderTask>.of( - ReaderTask.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( - (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( - (env) async => throw UnimplementedError(), - ); - - final result = apply.andThen( - () => ReaderTask.of(12.2), - ); - expect(() => result.run("abc"), - throwsA(const TypeMatcher())); - }); - }); - - group('call', () { - test('run a Task after another Task', () async { - final apply = ReaderTask( - (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((env) async => throw UnimplementedError())( - ReaderTask.of('abc'), - ); - - expect(() => apply.run("abc"), - throwsA(const TypeMatcher())); - }); - }); - - test('toReaderTaskEither', () async { - final apply = ReaderTask.of(10).toReaderTaskEither(); - - 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.Do( - (_) => _( - ReaderTask.of(10), - ), - ); - - final run = await doTask.run("abc"); - expect(run, 10); - }); - - test('should extract the correct values', () async { - final doTask = ReaderTask.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.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((r) => r.length); - - test('Monad', () { - expect(reader, isA()); - }); - - test('Applicative', () { - expect(reader, isA()); - }); - - test('Functor', () { - expect(reader, isA()); - }); - }); - - test('map', () { - final reader = Reader((r) => r.length); - final ap = reader.map((a) => a + 1); - final result = ap.run('abc'); - expect(result, 4); - }); - - test('map2', () { - final reader = Reader((r) => r.length); - final reader1 = Reader((r) => r.length * 2); - final ap = reader.map2(reader1, (a, c) => a * c); - final result = ap.run('abc'); - expect(result, 18); - }); - - test('map3', () { - final reader = Reader((r) => r.length); - final reader1 = Reader((r) => r.length * 2); - final reader2 = Reader((r) => r.length * 3); - final ap = - reader.map3(reader1, reader2, (a, c, d) => a * c * d); - final result = ap.run('ab'); - expect(result, 48); - }); - - test('ap', () { - final reader = Reader((r) => r.length); - final ap = - reader.ap(Reader((a) => (int n) => (n + a.length) / 2)); - final result = ap.run('abc'); - expect(result, 3.0); - }); - - test('flatMap', () { - final reader = Reader((r) => r.length); - final ap = - reader.flatMap((a) => Reader((b) => a + b.length)); - final result = ap.run('abc'); - expect(result, 6); - }); - - test('pure', () { - final reader = Reader((r) => r.length); - final ap = reader.pure(10); - final result = ap.run('abc'); - expect(result, 10); - }); - - test('andThen', () { - final reader = Reader((r) => r.length); - final ap = - reader.andThen(() => Reader((r) => r.length / 2)); - final result = ap.run('abc'); - expect(result, 1.5); - }); - - test('call', () { - final reader = Reader((r) => r.length); - final ap = reader(Reader((r) => r.length / 2)); - final result = ap.run('abc'); - expect(result, 1.5); - }); - - test('compose', () { - final reader = Reader((r) => r.length); - final ap = reader.compose(Reader((r) => r.length / 2)); - final result = ap.run('abc'); - expect(result, 1.5); - }); - - test('local', () { - final reader = Reader((r) => r.length); - final ap = reader.local((context) => '$context'); - final result = ap.run(7.5); - expect(result, 3); - }); - - test('ask', () { - final reader = Reader((r) => r.length); - final ap = reader.ask(); - final result = ap.run('abc'); - expect(result, 'abc'); - }); - - test('asks', () { - final reader = Reader((r) => r.length); - final ap = reader.asks((r) => r.length * 2); - final result = ap.run('abc'); - expect(result, 6); - }); - - test('flatten', () { - final reader = Reader>( - (r) => Reader((r) => r.length)); - final ap = Reader.flatten(reader); - expect(ap, isA>()); - final result = ap.run('abc'); - expect(result, 3); - }); - }); - - test('chainFirst', () async { - final task = Reader(((r) => r.length)); - var sideEffect = 10; - final chain = task.chainFirst((b) { - sideEffect = 100; - return Reader((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((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(); - 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(); - 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((a1, a2) => a1 + a2); - expect(instance.combineN(1, 10), 10); - }); - - test('.intercalate', () { - final instance = Semigroup.instance((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((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((a1, a2) => a1 + a2); - - test('Semigroup', () { - expect(instance, isA()); - }); - - test('Band', () { - expect(instance, isA()); - }); - - test('CommutativeSemigroup', () { - expect(instance, isA()); - }); - }); - - test('combineN (from Band)', () { - final instance = Semilattice.instance((a1, a2) => a1 + a2); - expect(instance.combineN(1, 1), 1); - expect(instance.combineN(1, 10), 2); - }); - - test('asMeetPartialOrder', () { - final instance = Semilattice.instance((a1, a2) => a1 + a2); - final eq = Eq.instance((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((a1, a2) => a1 + a2); - final eq = Eq.instance((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((s) async => (s.length, '${s}a')); - - test('Monad', () { - expect(state, isA()); - }); - - test('Applicative', () { - expect(state, isA()); - }); - - test('Functor', () { - expect(state, isA()); - }); - }); - - test('map', () async { - final state = StateAsync((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((s) async => (s.length, '${s}a')); - final state1 = StateAsync( - (s) async => (s.length / 2, '${s}b'), - ); - final ap = state.map2(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( - (s) async => (s.length, '${s}a'), - ); - final state1 = StateAsync( - (s) async => (s.length / 2, '${s}b'), - ); - final state2 = StateAsync( - (s) async => ('${s}aaa', '${s}b'), - ); - final ap = state.map3( - 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( - (s) async => (s.length, '${s}a'), - ); - final ap = state.ap( - 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( - (s) async => (s.length, '${s}a'), - ); - final ap = state.andThen( - () => StateAsync( - (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( - (s) async => (s.length, '${s}a'), - ); - final ap = state( - StateAsync( - (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.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((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, int>((s) async => (s.first, s.sublist(1))); - final ap = state.flatMap( - (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((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((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((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((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((s) async => (s.length, '${s}a')); - final result = await state.evaluate('aaa'); - expect(result, 3); - }); - - test('execute', () async { - final state = StateAsync((s) async => (s.length, '${s}a')); - final result = await state.execute('aaa'); - expect(result, 'aaaa'); - }); - - test('run', () async { - final state = StateAsync((s) async => (s.length, '${s}a')); - final result = await state.run('aaa'); - expect(result, isA()); - expect(result.$1, 3); - expect(result.$2, 'aaaa'); - }); - - test('flatten', () async { - final state = StateAsync>( - (s) async => ( - StateAsync( - (s) async => (s.length, '${s}a'), - ), - '${s}a', - ), - ); - final ap = StateAsync.flatten(state); - expect(ap, isA>()); - final result = await ap.run('aaa'); - expect(result.$1, 4); - expect(result.$2, 'aaaaa'); - }); - }); - - test('chainFirst', () async { - final state = StateAsync((s) async => (s.length, '${s}a')); - var sideEffect = 10; - final chain = state.chainFirst((b) { - sideEffect = 100; - return StateAsync((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((s) => (s.length, '${s}a')); - - test('Monad', () { - expect(state, isA()); - }); - - test('Applicative', () { - expect(state, isA()); - }); - - test('Functor', () { - expect(state, isA()); - }); - }); - - test('map', () { - final state = State((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((s) => (s.length, '${s}a')); - final state1 = State( - (s) => (s.length / 2, '${s}b'), - ); - final ap = state.map2(state1, (a, c) => c * a); - final result = ap.run('aaa'); - expect(result.$1, 6); - expect(result.$2, 'aaaab'); - }); - - test('map3', () { - final state = State( - (s) => (s.length, '${s}a'), - ); - final state1 = State( - (s) => (s.length / 2, '${s}b'), - ); - final state2 = State( - (s) => ('${s}aaa', '${s}b'), - ); - final ap = state.map3( - 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( - (s) => (s.length, '${s}a'), - ); - final ap = state.ap( - 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( - (s) => (s.length, '${s}a'), - ); - final ap = state.andThen( - () => State( - (s) => (s.length / 2, '${s}a'), - ), - ); - final result = ap.run('aaa'); - expect(result.$1, 2); - expect(result.$2, 'aaaaa'); - }); - - test('call', () { - final state = State( - (s) => (s.length, '${s}a'), - ); - final ap = state( - State( - (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((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((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, int>((s) => (s.first, s.sublist(1))); - final ap = state.flatMap( - (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((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((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((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((s) => (s.length, '${s}a')); - final ap = state.put('b'); - final result = ap.run('aaa'); - expect(result.$2, 'b'); - }); - - test('evaluate', () { - final state = State((s) => (s.length, '${s}a')); - final result = state.evaluate('aaa'); - expect(result, 3); - }); - - test('execute', () { - final state = State((s) => (s.length, '${s}a')); - final result = state.execute('aaa'); - expect(result, 'aaaa'); - }); - - test('run', () { - final state = State((s) => (s.length, '${s}a')); - final result = state.run('aaa'); - expect(result, isA()); - expect(result.$1, 3); - expect(result.$2, 'aaaa'); - }); - - test('flatten', () { - final state = State>( - (s) => ( - State( - (s) => (s.length, '${s}a'), - ), - '${s}a', - ), - ); - final ap = State.flatten(state); - expect(ap, isA>()); - final result = ap.run('aaa'); - expect(result.$1, 4); - expect(result.$2, 'aaaaa'); - }); - }); - - test('chainFirst', () { - final state = State((s) => (s.length, '${s}a')); - var sideEffect = 10; - final chain = state.chainFirst((b) { - sideEffect = 100; - return State((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((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((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((s) { - sideEffect++; - return ('a', s); - }), - State((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.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.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.tryCatch(() { - throw UnimplementedError(); - }, (error, _) { - expect(error, isA()); - 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.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.right(10); - final ap = task.flatMap(TaskEither.tryCatchK( - (n) => Future.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.right(10); - final ap = task.flatMap(TaskEither.tryCatchK((_) { - throw UnimplementedError(); - }, (error, _) { - expect(error, isA()); - 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(() async => Either.of(10)); - final ap = task.flatMap( - (r) => TaskEither(() 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(() async => Either.left('abc')); - final ap = task.flatMap( - (r) => TaskEither(() 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(() 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(() 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(() async => Either.of(10)); - final ap = task.bindEither(Either.of(2.5)); - final r = await ap.run(); - r.matchTestRight((r) => expect(r, 2.5)); - }); - - test('Left', () async { - final task = TaskEither(() async => Either.left('abc')); - final ap = task.bindEither(Either.of(2.5)); - final r = await ap.run(); - r.matchTestLeft((l) => expect(l, 'abc')); - }); - }); - - group('ap', () { - test('Right', () async { - final task = TaskEither(() async => Either.of(10)); - final ap = task - .ap(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(() async => Either.left('abc')); - final ap = task - .ap(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(() 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(() 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(() 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(() 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(() 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(() 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(() async => Either.of(10)); - final ap = task.map2( - TaskEither(() 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(() async => Either.left('abc')); - final ap = task.map2( - TaskEither(() 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(() async => Either.of(10)); - final ap = task.map3( - TaskEither(() async => Either.of(2)), - TaskEither(() 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(() async => Either.left('abc')); - final ap = task.map3( - TaskEither(() async => Either.of(2)), - TaskEither(() 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(() async => Either.of(10)); - final ap = task.andThen( - () => TaskEither(() 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(() async => Either.left('abc')); - final ap = task.andThen( - () => TaskEither(() 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(() async => Either.of(10)); - final ap = - task(TaskEither(() 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(() async => Either.left('abc')); - final ap = - task(TaskEither(() 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(() 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(() 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(() 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(() 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(() async => Either.of(10)); - final future = task.run(); - expect(future, isA()); - final r = await future; - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - group('fromEither', () { - test('Right', () async { - final task = TaskEither.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.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.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.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.fromNullable(10, () => "Error"); - final result = await task.run(); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final task = TaskEither.fromNullable(null, () => "Error"); - final result = await task.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('fromNullableAsync', () { - test('Right', () async { - final task = TaskEither.fromNullableAsync( - 10, Task(() async => "Error")); - final result = await task.run(); - result.matchTestRight((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final task = TaskEither.fromNullableAsync( - null, Task(() async => "Error")); - final result = await task.run(); - result.matchTestLeft((l) { - expect(l, "Error"); - }); - }); - }); - - group('fromPredicate', () { - test('True', () async { - final task = TaskEither.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.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.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.left('none'); - final r = await task.run(); - r.match((l) => expect(l, 'none'), (_) { - fail('should be left'); - }); - }); - - test('right', () async { - final task = TaskEither.right(10); - final r = await task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('leftTask', () async { - final task = TaskEither.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.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(() 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(() 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(() 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(() 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(() async => Either.of(10)); - final ex = - task.orElse((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(() async => Either.left('none')); - final ex = - task.orElse((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(() 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(() 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(() 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.of(10); - final r = await task.run(); - r.match((_) { - fail('should be right'); - }, (r) => expect(r, 10)); - }); - - test('flatten', () async { - final task = TaskEither>.of( - TaskEither.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.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(() 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(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right(2); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right(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(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return left("Error"); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right(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( - list, - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right("$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( - list, - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 - ? right("$a") - : left("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( - list, - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return right("$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( - list, - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect += 1; - return a % 2 == 0 - ? right("$a$i") - : left("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(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return right(2); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return right(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return right(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(1); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 1; - return left("Error"); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 2; - return right(3); - }), - TaskEither(() async { - await AsyncUtils.waitFuture(); - sideEffect = 3; - return right(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( - list, - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return right("$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( - list, - (a) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a - 1; - return a % 2 == 0 - ? right("$a") - : left("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( - list, - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return right("$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( - list, - (a, i) => TaskEither( - () async { - await AsyncUtils.waitFuture(); - sideEffect = a + i; - return a % 2 == 0 - ? right("$a$i") - : left("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.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.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.Do((_) async { - final a = await _(TaskEither.of(10)); - final b = await _(TaskEither.of(5)); - final c = await _(TaskEither.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.Do((_) { - _(TaskEither.of(10)); - throw UnimplementedError(); - }); - - expect( - doTaskEither.run, throwsA(const TypeMatcher())); - }); - - test('should rethrow if Left is thrown inside Do', () { - final doTaskEither = TaskEither.Do((_) { - _(TaskEither.of(10)); - throw Left('Error'); - }); - - expect(doTaskEither.run, throwsA(const TypeMatcher())); - }); - - test('should no execute past the first Left', () async { - var mutable = 10; - final doTaskEitherLeft = TaskEither.Do((_) async { - final a = await _(TaskEither.of(10)); - final b = await _(TaskEither.left("Error")); - mutable += 10; - return a + b; - }); - - final runLeft = await doTaskEitherLeft.run(); - expect(mutable, 10); - runLeft.matchTestLeft((l) { - expect(l, "Error"); - }); - - final doTaskEitherRight = TaskEither.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.tryCatch(() => Future.value(10)); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('Failure', () async { - final task = TaskOption.tryCatch(() => Future.error(10)); - final r = await task.run(); - expect(r, isA()); - }); - - test('throws Exception', () async { - final task = TaskOption.tryCatch(() { - throw UnimplementedError(); - }); - final r = await task.run(); - expect(r, isA()); - }); - }); - - group('tryCatchK', () { - test('Success', () async { - final task = TaskOption.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.of(10); - final ap = task.flatMap(TaskOption.tryCatchK( - (n) => Future.error(n + 5), - )); - final r = await ap.run(); - expect(r, isA()); - }); - - test('throws Exception', () async { - final task = TaskOption.of(10); - final ap = task.flatMap(TaskOption.tryCatchK((_) { - throw UnimplementedError(); - })); - final r = await ap.run(); - expect(r, isA()); - }); - }); - - group('flatMap', () { - test('Some', () async { - final task = TaskOption(() async => Option.of(10)); - final ap = - task.flatMap((r) => TaskOption(() async => Option.of(r + 10))); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 20)); - }); - - test('None', () async { - final task = TaskOption(() async => Option.none()); - final ap = - task.flatMap((r) => TaskOption(() async => Option.of(r + 10))); - final r = await ap.run(); - expect(r, isA()); - }); - }); - - group('ap', () { - test('Some', () async { - final task = TaskOption(() async => Option.of(10)); - final ap = task - .ap(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(() async => Option.none()); - final ap = task - .ap(TaskOption(() async => Option.of((int c) => c / 2))); - final r = await ap.run(); - expect(r, isA()); - }); - }); - - group('map', () { - test('Some', () async { - final task = TaskOption(() 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(() async => Option.none()); - final ap = task.map((r) => r / 2); - final r = await ap.run(); - expect(r, isA()); - }); - }); - - group('map2', () { - test('Some', () async { - final task = TaskOption(() async => Option.of(10)); - final ap = task.map2( - TaskOption(() 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(() async => Option.none()); - final ap = task.map2( - TaskOption(() async => Option.of(2)), (b, c) => b / c); - final r = await ap.run(); - expect(r, isA()); - }); - }); - - group('map3', () { - test('Some', () async { - final task = TaskOption(() async => Option.of(10)); - final ap = task.map3( - TaskOption(() async => Option.of(2)), - TaskOption(() 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(() async => Option.none()); - final ap = task.map3( - TaskOption(() async => Option.of(2)), - TaskOption(() async => Option.of(5)), - (b, c, d) => b * c / d); - final r = await ap.run(); - expect(r, isA()); - }); - }); - - group('andThen', () { - test('Some', () async { - final task = TaskOption(() async => Option.of(10)); - final ap = - task.andThen(() => TaskOption(() async => Option.of(12.5))); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 12.5)); - }); - - test('None', () async { - final task = TaskOption(() async => Option.none()); - final ap = - task.andThen(() => TaskOption(() async => Option.of(12.5))); - final r = await ap.run(); - expect(r, isA()); - }); - }); - - group('call', () { - test('Some', () async { - final task = TaskOption(() async => Option.of(10)); - final ap = task(TaskOption(() async => Option.of(12.5))); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 12.5)); - }); - - test('None', () async { - final task = TaskOption(() async => Option.none()); - final ap = task(TaskOption(() async => Option.of(12.5))); - final r = await ap.run(); - expect(r, isA()); - }); - }); - - test('pure', () async { - final task = TaskOption(() 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(() async => Option.of(10)); - final future = task.run(); - expect(future, isA()); - final r = await future; - r.matchTestSome((r) => expect(r, 10)); - }); - - group('fromEither', () { - test('Some', () async { - final task = TaskOption.fromEither(Either.of(10)); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('None', () async { - final task = TaskOption.fromEither(Either.left('none')); - final r = await task.run(); - expect(r, isA()); - }); - }); - - group('fromNullable', () { - test('Right', () async { - final task = TaskOption.fromNullable(10); - final result = await task.run(); - result.matchTestSome((r) { - expect(r, 10); - }); - }); - - test('Left', () async { - final task = TaskOption.fromNullable(null); - final result = await task.run(); - expect(result, isA()); - }); - }); - - group('fromPredicate', () { - test('True', () async { - final task = TaskOption.fromPredicate(20, (n) => n > 10); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 20)); - }); - - test('False', () async { - final task = TaskOption.fromPredicate(10, (n) => n > 10); - final r = await task.run(); - expect(r, isA()); - }); - }); - - test('fromTask', () async { - final task = TaskOption.fromTask(Task(() async => 10)); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('none()', () async { - final task = TaskOption.none(); - final r = await task.run(); - expect(r, isA()); - }); - - test('some()', () async { - final task = TaskOption.some(10); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - group('match', () { - test('Some', () async { - final task = TaskOption(() 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(() 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(() async => Option.of(10)); - final ex = task.getOrElse(() => -1); - final r = await ex.run(); - expect(r, 10); - }); - - test('None', () async { - final task = TaskOption(() async => Option.none()); - final ex = task.getOrElse(() => -1); - final r = await ex.run(); - expect(r, -1); - }); - }); - - group('orElse', () { - test('Some', () async { - final task = TaskOption(() async => Option.of(10)); - final ex = - task.orElse(() => TaskOption(() async => Option.of(-1))); - final r = await ex.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('None', () async { - final task = TaskOption(() async => Option.none()); - final ex = - task.orElse(() => TaskOption(() async => Option.of(-1))); - final r = await ex.run(); - r.matchTestSome((r) => expect(r, -1)); - }); - }); - - group('alt', () { - test('Some', () async { - final task = TaskOption(() 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(() 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.of(10); - final r = await task.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('flatten', () async { - final task = TaskOption>.of(TaskOption.of(10)); - final ap = TaskOption.flatten(task); - final r = await ap.run(); - r.matchTestSome((r) => expect(r, 10)); - }); - - test('delay', () async { - final task = TaskOption(() 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(); - }), - 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()); - expect(sideEffect, list.length); - }); - }); - - group('traverseList', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseList( - 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( - 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()); - expect(sideEffect, list.length); - }); - }); - - group('traverseListWithIndex', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseListWithIndex( - 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( - 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()); - 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(); - }), - 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()); - expect(sideEffect, 3); - }); - }); - - group('traverseListSeq', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseListSeq( - 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( - 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()); - expect(sideEffect, 5); - }); - }); - - group('traverseListWithIndexSeq', () { - test('Some', () async { - final list = [1, 2, 3, 4, 5, 6]; - var sideEffect = 0; - final traverse = TaskOption.traverseListWithIndexSeq( - 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( - 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()); - expect(sideEffect, 11); - }); - }); - - group('Do Notation', () { - test('should return the correct value', () async { - final doTaskOption = TaskOption.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.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.Do((_) async { - final a = await _(TaskOption.of(10)); - final b = await _(TaskOption.of(5)); - final c = await _(TaskOption.none()); - return a + b + c; - }); - final run = await doTaskOption.run(); - expect(run, isA()); - }); - - test('should rethrow if throw is used inside Do', () { - final doTaskOption = TaskOption.Do((_) { - _(TaskOption.of(10)); - throw UnimplementedError(); - }); - - expect( - doTaskOption.run, throwsA(const TypeMatcher())); - }); - - test('should rethrow if None is thrown inside Do', () { - final doTaskOption = TaskOption.Do((_) { - _(TaskOption.of(10)); - throw const None(); - }); - - expect(doTaskOption.run, throwsA(const TypeMatcher())); - }); - - test('should no execute past the first Left', () async { - var mutable = 10; - final doTaskOptionNone = TaskOption.Do((_) async { - final a = await _(TaskOption.of(10)); - final b = await _(TaskOption.none()); - mutable += 10; - return a + b; - }); - - final runNone = await doTaskOptionNone.run(); - expect(mutable, 10); - expect(runNone, isA()); - - final doTaskOptionSome = TaskOption.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>()); - 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>()); - 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()); - 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()); - 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(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( - 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>()); - 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())); - }); - }); - - 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(() async => throw UnimplementedError()); - final ap = task(Task.of('abc')); - expect(ap.run, throwsA(const TypeMatcher())); - }); - }); - - 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( - 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( - 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( - 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( - 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 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( - Map source, - void Function(Map 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> optionGenerator( - Generator source, { - double ratioNone = 0.1, - }) => - (random, size) => (random.nextDouble() > ratioNone - ? source.map(some) - : source.map((value) => none()))(random, size); - - /// [Generator] for `Option` - Generator> get optionInt => optionGenerator(any.int); - - /// [Generator] for `Option` - Generator> get optionDouble => optionGenerator(any.double); - - /// [Generator] for `Option` - Generator> 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> eitherGenerator( - Generator leftSource, - Generator rightSource, { - double ratioLeft = 0.5, - }) => - (random, size) => (random.nextDouble() > ratioLeft - ? leftSource.map>(left) - : rightSource.map>(right))(random, size); - - /// [Generator] for `Either` - Generator> 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 on Option { - /// 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 on Either { - /// 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 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 extends IEffect { /// {@category execution} Future> _runEffect(E? env) async => _unsafeRun(env); - /// {@category execution} - Future> call(E env) => _runEffect(env); - /// {@category execution} R runSync(E env) { final result = _unsafeRun(env); @@ -65,6 +64,15 @@ final class Effect extends IEffect { }; } + /// {@category execution} + Exit 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 runFuture(E env) async { final result = await _unsafeRun(env); @@ -74,6 +82,9 @@ final class Effect extends IEffect { }; } + /// {@category execution} + Future> runFutureExit(E env) async => _runEffect(env); + /// {@category constructors} // ignore: non_constant_identifier_names factory Effect.gen(DoFunctionEffect f) => Effect._( @@ -107,14 +118,14 @@ final class Effect extends IEffect { ); /// {@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 unit() => Effect._( - (_) async => Exit.success(null), + static Effect unit() => Effect._( + (_) => Exit.success(FUnit.unit), ); /// Extract the required dependency from the complete environment. @@ -139,6 +150,16 @@ final class Effect extends IEffect { ), ); + /// {@category mapping} + Effect 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 map(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 extends IEffect { const Either(); + R? toNullable(); + Option toOption(); + Either flatMap(covariant Either Function(R r) f) { return switch (this) { Left(value: final value) => Left(value), @@ -45,6 +48,21 @@ final class Right extends Either { Either orElse(covariant Either Function(L l) orElse) => Right(value); + + @override + R toNullable() => value; + + @override + Option 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 extends Either { @@ -59,4 +77,19 @@ final class Left extends Either { Either orElse(covariant Either Function(L l) orElse) => orElse(value); + + @override + R? toNullable() => null; + + @override + Option 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 extends Exit { 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 extends Exit { 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 on FutureOr { + FutureOr then(FutureOr Function(A a) f) { + if (this is Future) { + return (this as Future).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 extends IEffect { const Option(); + R? toNullable(); + + Either toEither(L Function() onLeft) => switch (this) { + Some(value: final value) => Right(value), + None() => Left(onLeft()), + }; + Option flatMap(covariant Option Function(R r) f) { return switch (this) { None() => None(), @@ -37,6 +44,21 @@ final class Some extends Option { Effect get asEffect => Effect.succeed(value); Option andThen(covariant Option Function() then) => then(); + + @override + R toNullable() => value; + + @override + Either toEither(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 { @@ -50,4 +72,16 @@ final class None extends Option { Effect get asEffect => Effect.fail(null as Never); Option andThen(covariant Option 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 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 extends IEffect { factory Effect.succeed(R value) => Effect._((_) => Exit.success(value)); /// {@category constructors} - static Effect unit() => Effect._( - (_) => Exit.success(FUnit.unit), + static Effect 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 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 { typedef DoAdapterEffect = Future Function(IEffect); DoAdapterEffect _doAdapter(E? env) => (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 extends IEffect { return "Effect(${_unsafeRun.runtimeType})"; } - /// {@category execution} - Future> _runEffect(E? env) async => _unsafeRun(env); - /// {@category execution} R runSync(E env) { final result = _unsafeRun(env); @@ -83,7 +81,7 @@ final class Effect extends IEffect { } /// {@category execution} - Future> runFutureExit(E env) async => _runEffect(env); + Future> runFutureExit(E env) async => _unsafeRun(env); /// {@category constructors} // ignore: non_constant_identifier_names @@ -103,9 +101,9 @@ final class Effect extends IEffect { 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 extends IEffect { /// {@category constructors} factory Effect.function(FutureOr Function() f) => Effect._( - (_) async => Exit.success(await f()), + (_) => f().then(Exit.success), ); /// {@category constructors} @@ -128,6 +126,56 @@ final class Effect extends IEffect { (_) => Exit.success(f_unit.unit), ); + /// {@category collecting} + static Effect> allIterable( + Iterable iterable, + Effect Function(A _) f, + ) => + Effect._( + (env) { + if (iterable.isEmpty) { + return Exit.success([]); + } + + return iterable + .map(f) + .fold>>( + 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> all( + Iterable> iterable, + ) => + Effect.allIterable( + iterable, + identity, + ); + + /// {@category zipping} + Effect zipWith( + Effect 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 extends IEffect { /// {@category do_notation} static Effect env() => Effect._( - (env) async => Exit.success(env!), + (env) => Exit.success(env!), ); /// {@category combining} @@ -165,27 +213,31 @@ final class Effect extends IEffect { /// {@category mapping} Effect mapError(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 mapBoth(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 flatMap(Effect 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 extends IEffect { /// {@category sequencing} Effect tapError(Effect Function(L l) f) => Effect._( - (env) async { - switch ((await _runEffect(env))) { - case Failure(value: final value): - await f(value)._unsafeRun(env); - return Exit.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 extends IEffect { Effect Function(L l) orElse, ) => Effect._( - (env) async => switch ((await _unsafeRun(env))) { - Failure(value: final value) => orElse(value)._unsafeRun(env), - Success(value: final value) => - Effect.succeed(value)._unsafeRun(env), - }, + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Failure(value: final value) => orElse(value)._unsafeRun(env), + Success(value: final value) => + Effect.succeed(value)._unsafeRun(env), + }, + ), ); /// {@category error_handling} @@ -228,11 +280,13 @@ final class Effect extends IEffect { Effect Function(L error) f, ) => Effect._( - (env) async => switch ((await _unsafeRun(env))) { - Failure(value: final value) => f(value)._unsafeRun(env), - Success(value: final value) => - Effect.succeed(value)._unsafeRun(env), - }, + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Failure(value: final value) => f(value)._unsafeRun(env), + Success(value: final value) => + Effect.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 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 extends IEffect { ); /// {@category collecting} - static Effect> allIterable( + static Effect> allIterable( Iterable iterable, Effect Function(A _) f, ) => @@ -146,18 +146,12 @@ final class Effect extends IEffect { (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> all( + static Effect> all( Iterable> iterable, ) => Effect.allIterable( From 7fc0a246d36fcc6a2a4e376e1b6b5d57bb2d407c Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 extends IEffect { }, ); + /// {@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 Function() f) => Effect._( (_) => f().then(Exit.success), @@ -122,8 +127,8 @@ final class Effect extends IEffect { factory Effect.succeed(R value) => Effect._((_) => Exit.success(value)); /// {@category constructors} - static Effect unit() => Effect._( - (_) => Exit.success(f_unit.unit), + static Effect 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 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 extends IEffect { ), ); + /// {@category alternatives} + Effect get orDie => Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Failure(value: final value) => + throw Exception("orDie effect ($value)"), + Success(value: final value) => + Effect.succeed(value)._unsafeRun(env), + }, + ), + ); + + /// {@category alternatives} + Effect orDieWith(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.succeed(value)._unsafeRun(env), + }, + ), + ); + /// {@category error_handling} Effect catchError( Effect 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())); + }); + }); + }, + ); +} From e66dd028711621e6c8102f9704783edd54091c3c Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 on Iterable { Option 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 on Iterable { /// **Note**: Because accessing the last element of an [Iterable] requires /// stepping through all the other elements, `lastOption` **can be slow**. Option get lastOption { - if (isEmpty) return const None(); + if (isEmpty) return None(); return Some(last); } @@ -47,7 +47,7 @@ extension FpdartOnIterable on Iterable { /// at that point, the returned iterable will also be empty, same /// as if this iterable has only one element. Option> get tail { - if (isEmpty) return const None(); + if (isEmpty) return None(); return Some(skip(1)); } @@ -61,7 +61,7 @@ extension FpdartOnIterable on Iterable { /// at that point, the returned iterable will also be empty, same /// as if this iterable has only one element. Option> get init { - if (isEmpty) return const None(); + if (isEmpty) return None(); return Some(this.dropRight(1)); } @@ -332,7 +332,7 @@ extension FpdartOnIterable on Iterable { } return Some(min); } - return const None(); + return None(); } /// The least element of this [Iterable] based on `order`. @@ -349,7 +349,7 @@ extension FpdartOnIterable on Iterable { } 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 on Map { 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 on Map { 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 on Map { Option extract(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 extends IEffect { const Option(); - R? toNullable(); + factory Option.safeCast(dynamic value) => + Option.safeCastStrict(value); - Either toEither(L Function() onLeft) => switch (this) { - Some(value: final value) => Right(value), - None() => Left(onLeft()), - }; + static Option safeCastStrict(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 flatMap(covariant Option 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 flatMap(covariant Option Function(R r) f); + Option ap( covariant Option f, ) => @@ -26,6 +42,11 @@ sealed class Option extends IEffect { ), ); + Either toEither(L Function() onLeft) => switch (this) { + Some(value: final value) => Right(value), + None() => Left(onLeft()), + }; + Option map(V Function(R r) f) => ap(Some(f)); Effect provide(L Function() onNone) => Effect._( @@ -45,6 +66,12 @@ final class Some extends Option { Option andThen(covariant Option Function() then) => then(); + @override + Option flatMap(covariant Option Function(R r) f) => f(value); + + @override + Effect provide(L Function() onNone) => Effect.succeed(value); + @override R toNullable() => value; @@ -62,7 +89,10 @@ final class Some extends Option { } final class None extends Option { - 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 { Option andThen(covariant Option Function() then) => this; @override - Null toNullable() => null; - - @override - bool operator ==(Object other) => other is None; + Option flatMap(covariant Option 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 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 extends IEffect { 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(value, onError); + + static Either safeCastStrict( + V value, + L Function(V value) onError, + ) => + value is R ? Right(value) : Left(onError(value)); + R? toNullable(); Option toOption(); - - Either flatMap(covariant Either Function(R r) f) { - return switch (this) { - Left(value: final value) => Left(value), - Right(value: final value) => f(value), - }; - } + Either flatMap(Either Function(R r) f); + Either mapLeft(C Function(L l) f); + Effect provide(); + Either mapBoth({ + required D Function(L l) onLeft, + required C Function(R r) onRight, + }); + Either get flip; + R getOrElse(R Function(L l) orElse); Either ap( - covariant Either f, + Either f, ) => f.flatMap( (f) => flatMap( @@ -23,18 +60,6 @@ sealed class Either extends IEffect { ); Either map(V Function(R r) f) => ap(Right(f)); - - Effect provide() => Effect._( - (env) => switch (this) { - Left(value: final value) => Exit.failure(value), - Right(value: final value) => Exit.success(value), - }, - ); - - Either mapLeft(C Function(L l) f) => switch (this) { - Left(value: final value) => Left(f(value)), - Right(value: final value) => Right(value), - }; } final class Right extends Either { @@ -44,10 +69,30 @@ final class Right extends Either { @override Effect get asEffect => Effect.succeed(value); - Either andThen(covariant Either Function() then) => then(); + Either andThen(Either Function() then) => then(); + + Either orElse(Either Function(L l) orElse) => Right(value); + + @override + R getOrElse(R Function(L l) orElse) => value; + + @override + Either get flip => Left(value); + + @override + Either mapBoth( + {required D Function(L l) onLeft, + required C Function(R r) onRight}) => + Right(onRight(value)); - Either orElse(covariant Either Function(L l) orElse) => - Right(value); + @override + Either mapLeft(C Function(L l) f) => Right(value); + + @override + Either flatMap(Either Function(R r) f) => f(value); + + @override + Effect provide() => Effect.succeed(value); @override R toNullable() => value; @@ -72,11 +117,30 @@ final class Left extends Either { @override Effect get asEffect => Effect.fail(value); - Either andThen(covariant Either Function() then) => - Left(value); + Either andThen(Either Function() then) => Left(value); + + Either orElse(Either Function(L l) orElse) => orElse(value); + + @override + R getOrElse(R Function(L l) orElse) => orElse(value); + + @override + Either get flip => Right(value); - Either orElse(covariant Either Function(L l) orElse) => - orElse(value); + @override + Either mapBoth( + {required D Function(L l) onLeft, + required C Function(R r) onRight}) => + Left(onLeft(value)); + + @override + Either mapLeft(C Function(L l) f) => Left(f(value)); + + @override + Either flatMap(Either Function(R r) f) => Left(value); + + @override + Effect provide() => 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 extends IEffect { } R? toNullable(); - - Option flatMap(covariant Option Function(R r) f); + Option flatMap(Option Function(R r) f); Option ap( - covariant Option f, + Option f, ) => f.flatMap( (f) => flatMap( @@ -64,10 +63,10 @@ final class Some extends Option { @override Effect get asEffect => Effect.succeed(value); - Option andThen(covariant Option Function() then) => then(); + Option andThen(Option Function() then) => then(); @override - Option flatMap(covariant Option Function(R r) f) => f(value); + Option flatMap(Option Function(R r) f) => f(value); @override Effect provide(L Function() onNone) => Effect.succeed(value); @@ -101,10 +100,10 @@ final class None extends Option { // ignore: cast_from_null_always_fails Effect get asEffect => Effect.fail(null as Never); - Option andThen(covariant Option Function() then) => this; + Option andThen(Option Function() then) => this; @override - Option flatMap(covariant Option Function(Never r) f) => this; + Option flatMap(Option Function(Never r) f) => this; @override Null toNullable() => null; From cb69054bd72c199ea78827b24749d6769d4a052a Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 { - final L value; - const _EffectThrow(this.value); + final Cause cause; + const _EffectThrow(this.cause); } typedef DoAdapterEffect = Future Function(IEffect); @@ -20,8 +20,8 @@ typedef DoAdapterEffect = Future Function(IEffect); DoAdapterEffect _doAdapter(E? env) => (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 extends IEffect { /// /// In practice a user of the library should never be allowed to pass `null` as [E]. final FutureOr> Function(E? env) _unsafeRun; + final StackTrace? stackTrace; - const Effect._(this._unsafeRun); + static bool debugTracing = false; + + const Effect._( + this._unsafeRun, { + this.stackTrace, + }); @override Effect get asEffect => this; @@ -53,12 +59,12 @@ final class Effect extends IEffect { 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() => throw Exception("Failed runSync Effect"), - Success(value: final value) => value, + Left(value: final cause) => throw cause, + Right(value: final value) => value, }; } @@ -66,7 +72,7 @@ final class Effect extends IEffect { Exit 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 extends IEffect { Future runFuture(E env) async { final result = await _unsafeRun(env); return switch (result) { - Failure() => throw Exception("Failed runFuture Effect"), - Success(value: final value) => value, + Left(value: final cause) => throw cause, + Right(value: final value) => value, }; } @@ -88,9 +94,9 @@ final class Effect extends IEffect { factory Effect.gen(DoFunctionEffect f) => Effect._( (env) async { try { - return Exit.success(await f(_doAdapter(env))); - } on _EffectThrow catch (e) { - return Exit.failure(e.value); + return Right(await f(_doAdapter(env))); + } on _EffectThrow catch (err) { + return Left(err.cause); } }, ); @@ -103,32 +109,32 @@ final class Effect extends IEffect { 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 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 unit() => Effect._( - (_) => Exit.success(fpdart_unit.unit), + (_) => Right(fpdart_unit.unit), ); /// {@category collecting} @@ -139,7 +145,7 @@ final class Effect extends IEffect { Effect._( (env) { if (iterable.isEmpty) { - return Exit.success([]); + return Right([]); } return iterable @@ -184,7 +190,7 @@ final class Effect extends IEffect { /// {@category do_notation} static Effect env() => Effect._( - (env) => Exit.success(env!), + (env) => Right(env!), ); /// {@category combining} @@ -201,8 +207,13 @@ final class Effect extends IEffect { Effect 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(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 extends IEffect { Effect mapError(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(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 extends IEffect { 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(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 extends IEffect { Effect flatMap(Effect 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 extends IEffect { Effect tapError(Effect 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(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 extends IEffect { 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(error: final error) => orElse(error)._unsafeRun(env), + Empty() => Left(cause), + Interrupt() => Left(cause), + Die() => Left(cause), + }, + Right(value: final value) => Effect.succeed(value)._unsafeRun(env), }, ), @@ -278,9 +310,8 @@ final class Effect extends IEffect { Effect 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.succeed(value)._unsafeRun(env), }, ), @@ -291,8 +322,14 @@ final class Effect extends IEffect { 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(error: final error) => + Left(Die.current(onError(error))), + Empty() => Left(cause), + Interrupt() => Left(cause), + Die() => Left(cause), + }, + Right(value: final value) => Effect.succeed(value)._unsafeRun(env), }, ), @@ -305,8 +342,13 @@ final class Effect extends IEffect { 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(error: final error) => f(error)._unsafeRun(env), + Empty() => Left(cause), + Interrupt() => Left(cause), + Die() => Left(cause), + }, + Right(value: final value) => Effect.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 { - const Exit(); - factory Exit.success(R value) => Success(value); - factory Exit.failure(L value) => Failure(value); +import 'effect.dart'; + +typedef Exit = Either, R>; + +sealed class Cause { + const Cause(); + + StackTrace? get stackTrace; + + Cause withTrace(StackTrace stack); +} + +/// Represents a lack of errors +final class Empty extends Cause { + @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 extends Exit { - final R value; - const Success(this.value); +final class Interrupt extends Cause { + @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 extends Exit { - final L value; - const Failure(this.value); +/// Failed as a result of a defect (unexpected error) +final class Die extends Cause { + 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 extends Cause { + final L error; + + @override + final StackTrace? stackTrace; + + const Fail(this.error, [this.stackTrace]); + + @override + Fail 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 extends IEffect { Effect provide(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())); }); }); @@ -29,7 +29,7 @@ void main() { test('fail', () { final main = Effect.fail(10).orDieWith((_) => CustomError()); - expect(() => main.runSync(null), throwsA(isA())); + expect(() => main.runSync(null), throwsA(isA())); }); }); }, From 4a85df32b44cbc0d76b423b9457a445854157ba6 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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.tryCatch( - () => Future.value(10), - (error, stackTrace) => "10", + execute: () => Future.value(10), + onError: (error, stackTrace) => "10", ); final effect1 = Effect.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 extends IEffect { ); /// {@category constructors} - factory Effect.tryCatch( - FutureOr Function() execute, - L Function(Object error, StackTrace stackTrace) onError, - ) => + factory Effect.tryCatch({ + required FutureOr Function() execute, + required L Function(Object error, StackTrace stackTrace) onError, + FutureOr 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 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 extends IEffect { Left(value: final cause) => switch (cause) { Fail(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 extends IEffect { Left(value: final cause) => switch (cause) { Fail(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 extends IEffect { Left(value: final cause) => switch (cause) { Fail(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 extends IEffect { (_) => 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 extends IEffect { Left(value: final cause) => switch (cause) { Fail(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 extends IEffect { Fail(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 extends IEffect { Left(value: final cause) => switch (cause) { Fail(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 { } } -final class Interrupt extends Cause { - @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 { final dynamic error; From 69af3834eb8536401c273ee6d7d92a121c37f92e Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 extends IEffect { ), ); + /// {@category zipping} + Effect zipLeft( + Effect effect, + ) => + flatMap( + (r) => effect.map( + (_) => r, + ), + ); + + /// {@category zipping} + Effect zipRight( + Effect effect, + ) => + flatMap((_) => effect); + /// Extract the required dependency from the complete environment. /// /// {@category do_notation} @@ -280,10 +296,6 @@ final class Effect extends IEffect { ), ); - /// {@category sequencing} - Effect andThen(Effect Function() then) => - flatMap((_) => then()); - /// {@category alternatives} Effect orElse( Effect 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 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 extends IEffect { ); /// {@category collecting} - static Effect> allIterable( + static Effect> forEach( Iterable iterable, - Effect Function(A _) f, + Effect Function(A a, int index) f, ) => Effect._( (env) { @@ -150,7 +150,7 @@ final class Effect extends IEffect { } return iterable - .map(f) + .mapWithIndex(f) .fold>>( Effect.succeed(Iterable.empty()), (acc, effect) => acc.zipWith( @@ -166,10 +166,7 @@ final class Effect extends IEffect { static Effect> all( Iterable> iterable, ) => - Effect.allIterable( - iterable, - identity, - ); + Effect.forEach(iterable, (a, _) => a); /// {@category zipping} Effect zipWith( 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([ + 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 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 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 extends HKT<_TaskHKT, A> + with Functor<_TaskHKT, A>, Applicative<_TaskHKT, A>, Monad<_TaskHKT, A> { + final Future 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 { + const IEffect(); + Effect 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 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 From bda39348e0feb61a94d88d8582d6e33bce24f4be Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 get( + Uri url, { + Map? 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 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 on Effect { Effect withEnv() => 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 runSyncExitNoEnv() { + final result = _unsafeRun(null); + if (result is Future) { + return Left(Die.current("")); + } + return result; + } + + /// {@category execution} + Future runFutureNoEnv() async { + final result = await _unsafeRun(null); + return switch (result) { + Left(value: final cause) => throw cause, + Right(value: final value) => value, + }; + } + + /// {@category execution} + Future> runFutureExitNoEnv() async => _unsafeRun(null); } From f39ee8bc6ff4ffe4182aa4af9b42ba9effdc5f09 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 $>( + 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 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 extends IEffect { 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 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 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 $>( + final bodyJsonMap = $.sync>( 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.tryCatch( - execute: () => Future.value(10), - onError: (error, stackTrace) => "10", - ); - - final effect1 = Effect.function(() => 10); - - final main = Effect.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(10).provide()); - final asOption = await _(Some(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 = ({ + FutureOr Function(IEffect) async, + A Function(IEffect) sync, +}); + final class _EffectThrow { final Cause cause; const _EffectThrow(this.cause); -} -typedef DoAdapterEffect = Future Function(IEffect); + @override + String toString() { + return "Effect.gen error: $cause"; + } +} -DoAdapterEffect _doAdapter(E? env) => (effect) => Future.sync( - () => effect.asEffect._unsafeRun(env).then( - (exit) => switch (exit) { - Left(value: final cause) => throw _EffectThrow(cause), - Right(value: final value) => value, - }, +EffectGen _effectGen(E? env) => ( + async: (effect) => Future.sync( + () => effect.asEffect._unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => throw _EffectThrow(cause), + Right(value: final value) => value, + }, + ), ), + sync: (effect) { + final run = effect.asEffect._unsafeRun(env); + if (run is Future) { + throw _EffectThrow( + 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 = Future Function(DoAdapterEffect _); +typedef DoFunctionEffect = FutureOr Function( + EffectGen $, +); abstract interface class IEffect { const IEffect(); @@ -92,9 +117,9 @@ final class Effect extends IEffect { /// {@category constructors} // ignore: non_constant_identifier_names factory Effect.gen(DoFunctionEffect f) => Effect._( - (env) async { + (env) { try { - return Right(await f(_doAdapter(env))); + return f(_effectGen(env)).then(Right.new); } on _EffectThrow 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.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())); + }); + }); }); }, ); From 06fff824a6bb409765ae4523f78180f1af522a4e Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 get(Uri uri); } -Effect<(HttpClient, JsonCodec), PokemonError, Pokemon> program( +class Http implements HttpClient { + @override + Effect 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 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>( - Either.safeCastStrict( + Either.safeCastStrict, 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 on Effect { /// {@category execution} Future> runFutureExitNoEnv() async => _unsafeRun(null); } + +extension ProvideVoid on Effect { + /// {@category execution} + Effect provideVoid() => 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 extends IEffect { +sealed class Either extends IEffect { 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 on FutureOr { - FutureOr then(FutureOr Function(A a) f) { - if (this is Future) { - return (this as Future).then(f); - } - - return f(this as A); - } + FutureOr then(FutureOr Function(A a) f) => switch (this) { + final Future self => self.then(f), + final A self => f(self), + }; } From aa0d3a5eaa1c8567c99b1acada5db87951a6de6b Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 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>( Either.safeCastStrict, 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 extends IEffect { ); /// {@category constructors} - factory Effect.tryCatch({ + static Effect tryCatch({ required FutureOr Function() execute, required L Function(Object error, StackTrace stackTrace) onError, FutureOr Function()? onCancel, @@ -143,20 +143,24 @@ final class Effect extends IEffect { ); /// {@category constructors} - factory Effect.fromNullable(R? value, L Function() onNull) => Effect._( + static Effect fromNullable(R? value, L Function() onNull) => + Effect._( (_) => value == null ? Left(Fail(onNull())) : Right(value), ); /// {@category constructors} - factory Effect.function(FutureOr Function() f) => Effect._( + static Effect function(FutureOr Function() f) => + Effect._( (_) => f().then(Right.new), ); /// {@category constructors} - factory Effect.fail(L value) => Effect._((_) => Left(Fail(value))); + static Effect fail(L value) => + Effect._((_) => Left(Fail(value))); /// {@category constructors} - factory Effect.succeed(R value) => Effect._((_) => Right(value)); + static Effect succeed(R value) => + Effect._((_) => Right(value)); /// {@category constructors} static Effect 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 extends IEffect { +sealed class Either extends IEffect { const Either(); /// If calling `predicate` with `r` returns `true`, then return `Right(r)`. @@ -67,7 +67,7 @@ final class Right extends Either { const Right(this.value); @override - Effect get asEffect => Effect.succeed(value); + Effect get asEffect => Effect.succeed(value); Either andThen(Either Function() then) => then(); @@ -115,7 +115,7 @@ final class Left extends Either { const Left(this.value); @override - Effect get asEffect => Effect.fail(value); + Effect get asEffect => Effect.fail(value); Either andThen(Either 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 extends IEffect { +sealed class Option extends IEffect { const Option(); factory Option.safeCast(dynamic value) => @@ -61,16 +61,16 @@ final class Some extends Option { const Some(this.value); @override - Effect get asEffect => Effect.succeed(value); + Effect get asEffect => Effect.succeed(value).orDie; + + @override + Effect provide(L Function() onNone) => Effect.succeed(value); Option andThen(Option Function() then) => then(); @override Option flatMap(Option Function(R r) f) => f(value); - @override - Effect provide(L Function() onNone) => Effect.succeed(value); - @override R toNullable() => value; @@ -98,7 +98,7 @@ final class None extends Option { /// **This will always throw, don't use it!** // ignore: cast_from_null_always_fails - Effect get asEffect => Effect.fail(null as Never); + Effect get asEffect => Effect.fail(null as Never); Option andThen(Option Function() then) => this; From 4da33a1869afc3afa148f9eb10dc4bdba82f40df Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 get(Uri uri); + Effect get(Uri uri); } class Http implements HttpClient { @override - Effect get(Uri uri) => Effect.gen( + Effect get(Uri uri) => Effect.gen( ($) async { final response = await $.async(Effect.tryCatch( execute: () => http.get(uri), @@ -42,7 +42,7 @@ Effect 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 extends IEffect { ); /// {@category constructors} - static Effect tryCatch({ + factory Effect.tryCatch({ required FutureOr Function() execute, required L Function(Object error, StackTrace stackTrace) onError, FutureOr Function()? onCancel, @@ -143,24 +143,20 @@ final class Effect extends IEffect { ); /// {@category constructors} - static Effect fromNullable(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 function(FutureOr Function() f) => - Effect._( + factory Effect.function(FutureOr Function() f) => Effect._( (_) => f().then(Right.new), ); /// {@category constructors} - static Effect fail(L value) => - Effect._((_) => Left(Fail(value))); + factory Effect.fail(L value) => Effect._((_) => Left(Fail(value))); /// {@category constructors} - static Effect succeed(R value) => - Effect._((_) => Right(value)); + factory Effect.succeed(R value) => Effect._((_) => Right(value)); /// {@category constructors} static Effect unit() => Effect._( @@ -429,8 +425,3 @@ extension ProvideNever on Effect { /// {@category execution} Future> runFutureExitNoEnv() async => _unsafeRun(null); } - -extension ProvideVoid on Effect { - /// {@category execution} - Effect provideVoid() => 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 extends IEffect { +sealed class Either extends IEffect { const Either(); /// If calling `predicate` with `r` returns `true`, then return `Right(r)`. @@ -67,7 +67,7 @@ final class Right extends Either { const Right(this.value); @override - Effect get asEffect => Effect.succeed(value); + Effect get asEffect => Effect.succeed(value); Either andThen(Either Function() then) => then(); @@ -115,7 +115,7 @@ final class Left extends Either { const Left(this.value); @override - Effect get asEffect => Effect.fail(value); + Effect get asEffect => Effect.fail(value); Either andThen(Either 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 extends IEffect { +sealed class Option extends IEffect { const Option(); factory Option.safeCast(dynamic value) => @@ -61,7 +61,7 @@ final class Some extends Option { const Some(this.value); @override - Effect get asEffect => Effect.succeed(value).orDie; + Effect get asEffect => Effect.succeed(value); @override Effect provide(L Function() onNone) => Effect.succeed(value); @@ -98,7 +98,7 @@ final class None extends Option { /// **This will always throw, don't use it!** // ignore: cast_from_null_always_fails - Effect get asEffect => Effect.fail(null as Never); + Effect get asEffect => Effect.fail(null as Never); Option andThen(Option Function() then) => this; From 1ed678d765fd7f981881d23dbf5ffa46d001b5c7 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 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 program( Either.tryCatch( execute: () => json.decode(body), onError: (_, __) => const PokemonJsonDecodeError(), - ).provide(), + ), ); final bodyJsonMap = $.sync>( Either.safeCastStrict, 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 extends Either { const Right(this.value); @override - Effect get asEffect => Effect.succeed(value); + Effect get asEffect => Effect._((_) => Right(value)); Either andThen(Either Function() then) => then(); @@ -115,7 +115,7 @@ final class Left extends Either { const Left(this.value); @override - Effect get asEffect => Effect.fail(value); + Effect get asEffect => Effect._((_) => Left(Fail(value))); Either andThen(Either Function() then) => Left(value); From 7f3314edd4657ae44d30f2eb6a86f040355cc33a Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 get(Uri uri); + Effect get(Uri uri); } class Http implements HttpClient { @override - Effect get(Uri uri) => Effect.gen( + Effect get(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 extends Option { const Some(this.value); @override - Effect get asEffect => Effect.succeed(value); + Effect get asEffect => Effect._((_) => Right(value)); @override Effect provide(L Function() onNone) => Effect.succeed(value); @@ -94,11 +94,9 @@ final class None extends Option { factory None() => _none; @override - @internal - - /// **This will always throw, don't use it!** - // ignore: cast_from_null_always_fails - Effect get asEffect => Effect.fail(null as Never); + Effect get asEffect => + // ignore: cast_from_null_always_fails + Effect._((_) => Left(Fail(null as Never))); Option andThen(Option Function() then) => this; From a63aae1956299e0f13bceeb90eec1815ea4686a6 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 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 = ({ A Function(IEffect) sync, }); -final class _EffectThrow { +final class _EffectThrow implements Exception { final Cause cause; const _EffectThrow(this.cause); @@ -29,7 +28,7 @@ EffectGen _effectGen(E? env) => ( async: (effect) => Future.sync( () => effect.asEffect._unsafeRun(env).then( (exit) => switch (exit) { - Left(value: final cause) => throw _EffectThrow(cause), + Left(value: final cause) => throw _EffectThrow(cause), Right(value: final value) => value, }, ), @@ -43,7 +42,7 @@ EffectGen _effectGen(E? env) => ( } return switch (run) { - Left(value: final cause) => throw _EffectThrow(cause), + Left(value: final cause) => throw _EffectThrow(cause), Right(value: final value) => value, }; }, @@ -115,14 +114,16 @@ final class Effect extends IEffect { Future> runFutureExit(E env) async => _unsafeRun(env); /// {@category constructors} - // ignore: non_constant_identifier_names factory Effect.gen(DoFunctionEffect f) => Effect._( (env) { - try { - return f(_effectGen(env)).then(Right.new); - } on _EffectThrow catch (err) { - return Left(err.cause); - } + return f(_effectGen(env)).then( + Right.new, + onError: (dynamic error, dynamic stackTrack) { + if (error is _EffectThrow) { + return Left, 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 = Either, R>; -sealed class Cause { +sealed class Cause implements Error { const Cause(); - - StackTrace? get stackTrace; - Cause 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 on FutureOr { - FutureOr then(FutureOr Function(A a) f) => switch (this) { - final Future self => self.then(f), + FutureOr then(FutureOr Function(A a) f, {Function? onError}) => + switch (this) { + final Future self => self.then(f, onError: onError), final A self => f(self), }; } From e82dba66287a8b4e48acacaee10a1bac50f6db0a Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 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 program( }); void main() async { - final exit = await program("72jj1") + final exit = await program("9722") .map((pokemon) => print(pokemon)) - .catchError( + .catchError( (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 extends IEffect { /// {@category execution} Future 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> runFutureExit(E env) async => _unsafeRun(env); + Future> 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 f) => Effect._( @@ -366,8 +380,8 @@ final class Effect extends IEffect { ); /// {@category error_handling} - Effect catchError( - Effect Function(L error) f, + Effect catchError( + Effect Function(L error) f, ) => Effect._( (env) => _unsafeRun(env).then( @@ -382,6 +396,20 @@ final class Effect extends IEffect { }, ), ); + + /// {@category error_handling} + Effect catchCause( + Effect Function(Cause cause) f, + ) => + Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => f(cause), + Right(value: final value) => Effect.succeed(value), + } + ._unsafeRun(env), + ), + ); } extension ProvideNever on Effect { From 05036f1d6c27925271d3da42a97ff00ecb5b56c9 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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( - (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 extends IEffect { /// /// In practice a user of the library should never be allowed to pass `null` as [E]. final FutureOr> Function(E? env) _unsafeRun; - final StackTrace? stackTrace; - static bool debugTracing = false; - - const Effect._( - this._unsafeRun, { - this.stackTrace, - }); + const Effect._(this._unsafeRun); @override Effect get asEffect => this; @@ -83,7 +77,7 @@ final class Effect extends IEffect { 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 extends IEffect { ); /// {@category constructors} - factory Effect.function(FutureOr Function() f) => Effect._( + factory Effect.functionFail(FutureOr> Function() f) => Effect._( + (_) => f().then(Left.new), + ); + + /// {@category constructors} + factory Effect.functionSucceed(FutureOr Function() f) => Effect._( (_) => f().then(Right.new), ); @@ -174,9 +173,9 @@ final class Effect extends IEffect { factory Effect.succeed(R value) => Effect._((_) => Right(value)); /// {@category constructors} - static Effect unit() => Effect._( - (_) => Right(fpdart_unit.unit), - ); + static Effect unit = Effect._( + (_) => const Right(fpdart_unit.unit), + ); /// {@category collecting} static Effect> forEach( @@ -186,13 +185,13 @@ final class Effect extends IEffect { Effect._( (env) { if (iterable.isEmpty) { - return Right([]); + return const Right([]); } return iterable .mapWithIndex(f) .fold>>( - 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 extends IEffect { Effect 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.succeed(value)._unsafeRun(env), }, @@ -424,7 +423,7 @@ extension ProvideNever on Effect { 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 on Effect { /// {@category execution} Future 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 on Effect { } /// {@category execution} - Future> runFutureExitNoEnv() async => _unsafeRun(null); + Future> 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([ 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 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 _effectGen(E? env) => ( final run = effect.asEffect._unsafeRun(env); if (run is Future) { throw _EffectThrow( - Die.current("Cannot execute a Future using sync"), + Die.current( + Exception("gen.sync cannot execute async Effect"), + ), ); } @@ -75,50 +77,77 @@ final class Effect extends IEffect { /// {@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 { + 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 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 catch (cause) { + return Left(cause); + } catch (error, stackTrace) { + return Left(Die(error, stackTrace)); } - return result; } /// {@category execution} Future 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 { + 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> 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 catch (cause) { + return Left(cause); + } catch (error, stackTrace) { + return Left(Die(error, stackTrace)); } - return result; } /// {@category constructors} @@ -172,6 +201,17 @@ final class Effect extends IEffect { /// {@category constructors} factory Effect.succeed(R value) => Effect._((_) => Right(value)); + /// {@category constructors} + static Effect die(dynamic defect) => Effect._( + (_) => Left(Die.current(defect)), + ); + + /// {@category constructors} + static Effect functionDie(dynamic Function() run) => + Effect._( + (_) => Left(Die.current(run())), + ); + /// {@category constructors} static Effect unit = Effect._( (_) => const Right(fpdart_unit.unit), @@ -420,49 +460,22 @@ extension ProvideNever on Effect { ); /// {@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._( + (_) => _unsafeRun(null), + ).runSync(null); /// {@category execution} - Exit runSyncExitNoEnv() { - final result = _unsafeRun(null); - if (result is Future) { - return Left(Die.current("")); - } - return result; - } + Exit runSyncExitNoEnv() => Effect._( + (_) => _unsafeRun(null), + ).runSyncExit(null); /// {@category execution} - Future 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 runFutureNoEnv() async => Effect._( + (_) => _unsafeRun(null), + ).runFuture(null); /// {@category execution} - Future> 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> runFutureExitNoEnv() async => Effect._( + (_) => _unsafeRun(null), + ).runFutureExit(null); } From 0f91eeb6b93d9c3062c931f03bbc856fa9a7a5b0 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 extends IEffect { (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Fail(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 extends IEffect { (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Fail(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 extends IEffect { (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Fail(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 extends IEffect { Fail(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 extends IEffect { (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Fail(error: final error) => orElse(error)._unsafeRun(env), - Empty() => Left(cause), Die() => Left(cause), }, Right(value: final value) => @@ -409,7 +404,6 @@ final class Effect extends IEffect { Left(value: final cause) => switch (cause) { Fail(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 extends IEffect { (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Fail(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 = Either, R>; sealed class Cause implements Error { const Cause(); - Cause withTrace(StackTrace stack); -} - -/// Represents a lack of errors -final class Empty extends Cause { - @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 { 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 extends Cause { const Fail(this.error, [this.stackTrace]); - @override - Fail 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 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 { } final class Effect extends IEffect { - /// `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> Function(E? env) _unsafeRun; @@ -153,14 +153,18 @@ final class Effect extends IEffect { /// {@category constructors} factory Effect.gen(DoFunctionEffect f) => Effect._( (env) { - return f(_effectGen(env)).then( - Right.new, - onError: (dynamic error, dynamic stackTrack) { - if (error is _EffectThrow) { - return Left, R>(error.cause); - } - }, - ); + try { + return f(_effectGen(env)).then( + Right.new, + onError: (dynamic error) { + if (error is _EffectThrow) { + return Left, R>(error.cause); + } + }, + ); + } on _EffectThrow catch (genError) { + return Left(genError.cause); + } }, ); @@ -297,7 +301,7 @@ final class Effect extends IEffect { ); /// {@category mapping} - Effect get flip => Effect._( + Effect 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.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 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 extends IEffect { ); /// {@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 extends IEffect { ); /// {@category constructors} - static Effect unit = Effect._( - (_) => const Right(fpdart_unit.unit), - ); + static Effect unit() => Effect._( + (_) => const Right(fpdart_unit.unit), + ); /// {@category collecting} static Effect> forEach( @@ -300,6 +301,101 @@ final class Effect extends IEffect { ), ); + /// {@category conversions} + Effect> either() => Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => switch (cause) { + Fail(error: final error) => Right(Left(error)), + Die() => Left(cause), + }, + Right(value: final value) => Right(Right(value)), + }, + ), + ); + + /// {@category conversions} + Effect> option() => Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => switch (cause) { + Fail() => Right(None()), + Die() => Left(cause), + }, + Right(value: final value) => Right(Some(value)), + }, + ), + ); + + /// {@category conversions} + Effect> exit() => Effect._( + (env) => _unsafeRun(env).then( + (exit) => Right(exit), + ), + ); + + /// {@category folding} + Effect match({ + 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(error: final error) => Right(onFailure(error)), + Die() => Left(cause), + }, + Right(value: final value) => Right(onSuccess(value)), + }, + ), + ); + + /// {@category folding} + Effect matchCause({ + required C Function(Cause 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 matchEffect({ + required Effect Function(L l) onFailure, + required Effect Function(R r) onSuccess, + }) => + Effect._( + (env) => _unsafeRun(env).then( + (exit) => switch (exit) { + Left(value: final cause) => switch (cause) { + Fail(error: final error) => onFailure(error)._unsafeRun(env), + Die() => Left(cause), + }, + Right(value: final value) => onSuccess(value)._unsafeRun(env), + }, + ), + ); + + /// {@category folding} + Effect matchCauseEffect({ + required Effect Function(Cause l) onFailure, + required Effect 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 flip() => Effect._( (env) => _unsafeRun(env).then( @@ -329,6 +425,16 @@ final class Effect extends IEffect { ), ); + /// {@category mapping} + Effect mapErrorCause(C Function(Cause 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 mapBoth(C Function(L l) fl, D Function(R r) fr) => Effect._( From ab71d1eb68dc0b881e2925294410dd9fddb26618 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 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 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 extends HKT<_TaskHKT, A> - with Functor<_TaskHKT, A>, Applicative<_TaskHKT, A>, Monad<_TaskHKT, A> { - final Future 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 { - const IEffect(); - Effect 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 @@

-Functional programming in Dart and Flutter +Functional Effect System in Dart and Flutter (v2)

-All the main functional programming types and patterns fully documented, tested, and with examples +Functional Effect System fully documented, tested, and with examples, to write complex type-safe dart applications

@@ -35,11 +35,13 @@ All the main functional programming types and patterns fully documented<

+> 🏗️ 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(10); +final option = Some(10); + +final effect = Effect.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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 V`LiZviSYbURn$_bk~4q*{{Y(500IC2 literal 153192 zcmZsC1yodR*ES$Z$IwGbcMaXb3=9o|bO{5}EnNb_NGd7a4N7-O3@P0$Al)Shi2nG# zpO5;y|5|6k&0(!`_Py)6_TKjid#oylgGr8wgoK2nATOhVgoMhAgmjM*{lVRxoT8BJ zyI;u9HRQlZWy6#^NJunD3Nq4~p2qv>Xbn`dbG=(kv{6z7;rzvHG%PeMn4zIW)`F5d z@>udmvL!U~fSuO+=qSk6K>_jr&6F&D9F^j1LYlyH_YYf3OLOVT>2s`NKEsWd-!E5b zFEjYW&W0V`FBh*e>s@cZtG(E>pz?S1@W=`KVrR1#PfpfC9i?p?AkjOnt$_h74gAQ0 zz)rh|LWA_5YxE_CtJ@co&tk*p8OYTD_61McsfSGfy4tegvg^1KfwqtRM(G^pu5SYX zmiHFNhn_-ScGIr9cwN`+7=HA%96!9kF%mtCLN`>0Y#zpg9@ zI${C8-WKdExaRDwPF92Dut-auz2ax;nv*rA4u)EylbzHZx`yyKA-|sVS_CpzU$lv& zNYWCN6b#Ay_`m)AcbAVNq!y!GIiCc_eRk-}{W4AS?yHeps@rjndu-Mi0Q>+IyVsVr z(?Prqf&W2Hjy&H2a2$*qb@p4BJE^ihV<0{m&Lq1mMtQ|AlOqv*_{hTdzS`*>A!<|| zCa_hdJ+v*RY|hj-jMo1Rt;b^(4qC_><^L(=|5Q_py5?91QSV;Ln>pm*GD!^3N&iVT77 z>yH$UUDS(DZWZE5IE@oKs>9uBYaydn#nEF1sF6JvP8n1)apZ`-I%BmA3%O>~QFSbI z6K4Y?AS3_@{lAwL24Z0c+36La=tGUO;L&pHq)sxq^(%DA zv|88k!8dZ8qwWsGwh)+}>LMkKAB>hB^y^7?&pX#d!LhG0h{|2&-*wzf_b#%`BeuRh zOt&ztrO%TtMMSz0-%!ESq(hlcet6q0LnHrdvAdt6 zV(2xFSkq!(ftKnoJQlU3RySvwbJ#|3g$5#?wVLh@efl{1&Y2w%*%J~u9YJPlQ1KR*yJv@ynJysS9=^);dir&wHvdesd|MFrP;J0n z>|*Ltx~jZPd=uyY;^}~XbY4a0W^-=8&pw~5?Qwjbadxta++Rhr4pJ- zI4}wW_#OcICZx-`q$79IwtXxzPQ0j5)Xn)>t!KkMVyxgfX9P&>9Qjw6-l=0nDNBH& zm)cD0EAFC4xWY}v>b|XoJwVV&ZV1#zwwhOfh=c}5Zpvc+h=P5*n8&@>sCP6w@Lq3e zkGn~CjJ(=_PtmVIA`2~RA2|Y*Q2M`dmX(NKafs{FFK)mawP^}{m@S+7;?i`VucjVd z`$!vns``o2Mzhm&5^Hjp2* zY40!VSnHAiT7BC@fZR>V#Woh|dkqI>r3PNumi0#VfZ4`{7Q>;i1q16zc(X_PGc^aa zUIa~4qG`c ztJn*Y04jY}stBh~;QNRDODkgTho<%$4X?Wz=eX)lw`cvhMEQ2p55y};a6a3qO;17CmBW;>naM#NL1VRls_yr@rnD?J7_o6+6ba{OOgU2s=J3dicL8{^x@HC z4uCE*$HV(`0%7?#1+_RCq|{D}TOFY5%+(eoK=bR_gzBr-ob2A(3HHqhg@1z6l^Xd& zy3W?zC#HxT2kBh*0+f@z#ZtF>Gp}2fgs_(M1(oC}=QPUMCE!Ww27Ol0yA62@ zR?WC)m8FvGkc$TPe+z;F!n^6+eGNsATgxdvaHYRHugWe@SgC~b@e7!T^-ApqxgSB91^N%dcY?xEwR+E20Eua==ZnWa|}uw zGH~4|9kfMitME2)W?Pk*v08h|=`B#PfCmJioFfG)(enl6dii#ziqtYXp@k)VMxA`Q ziIP%b4WS+U*BmW}Z2S*?Aoqz*a}8C&3-4yjH)r7Sm&y{eS&oST#OsWxss`32q_2tN|~ELKP? zc$;2LQt?Ngnpeo03FkvjRdb_isVg6j5|f*Bf|zF})rKrcsn!P?=x9fz^$<{8g#(Sj z&^M$Yj#P{KC@-uQZc3p`cb@}J!!~n|?fhPJVtlfeOx6oR8zY*F!!eX!X2~KY2>pP! z8xsAkVA44#Y~i)8m_p%4dN6$?w_|x`7Ou+SSTM~&2LOd4p90&kTLiJZv)>FST*b7 zn?9Yp5MfAxsEl=lL54t-`!zp0kF+I9jqKz|m_DGE7uXJ!uj$%)YL$@zs zVDsYh8dSwJx4`PLpSO?w^9egT!^bIN3k+8AcL^F5`-ZFeg1SjMhGtgq6(Q%i>X(;I zuboy+4EhRX4siTcoO3R|(#G|O{Ka5Kvr%oZa0M=lYI}v8-_PC}sFaPhdzUN>KNTxg zCE?%Kk0MOYYL+49ccHz90?)77r#Ui|+ncZs z7yIu=;Jtp1dzeX&^<}nVo|MJLH8u-i65+`>uT~GnZmUoj+-^EK{2*AvXe&$#t+0}6 zIj?hz^aPM|5%33|Usr}>QtO1(maiyHpUL2*O8C?X9+5rJRvP<n z4%|D>5=iew3>o_sYm>Ww(pC;Uq?{_$lj8&lMxY6}Sj`fL4b;|U9zIe_j3}k0&6S{o z`+b^k2>|SSjjcJw3`EsY2(OG;nluXdzWHA0o4rA(fbc&mv{YaD4Gv>$7x?Y1AhFwRoew)gbBk*f#Syr*2 zIrO;a>3rjZp}swa+;o*ta&x`^zVL*onH*nsH1vRRoJ*QMHrn))`PA$S%VwvSLbC0f z+SlK3%yvwGtIU^te^TFTu0XgZCe>&PWQsu2 z<~64+GkiD&Ur?XM#`RqmfTrkc7%2~ENo`>0X1zC z6se)k({PqM(3evTl6K6~aB=YLD69HipI*)ycx_NQ%RsNIS&hX#mRnp2!=g}^ zjjD34M2(962CVFv-nCTS+nbd4$(_XK-8ZNRX!YDeCo>PTKpFO@ZB=Qh;P$9;UxtO4 zeVBT4#gZNmw9`GqJtetTY|8h!o0k6w%WifscoKlaJ4!E9_?LgDTgAo;wSxp4zy54MKG5)*%1h zud#f)z1q}QL~b%b`YU>M_v*WFrZ!lxwY2Ze35kGVE5GQhSgr!-<#hJ}LCsTc+`D%P z5kLE}&1r)FAgVlg$?&SloqA>nD|d2$P*pCBRQv7p0}R`GZFp}K^P}2CeKVnXy`_=n zBEC{v?BRc^P^3U=<@Ppfo4Iwm6pd!~da6Q_sGC{snMjQvZ7`X_Q1aLFvaC^d9H!DA z(k#AL_Dr34G}n}r<3vQOx9XKN_Ht~lb1)r0p$m@njIt&Lkpp45jj^a~<+tlYi)~TxCzk$}Us7sqBGu*1g}kcDoYQ#_>3JNIaGi zaFOv5d-XUWoAtF=`5YjETE87P6x9ecdw5)l2iKr#+Bgu`)p#l-EeXMYLram==-K# zw>?6e@h}*i&b)>uJ`WF*>^%{8jhPobA}RIeYd?#Jb_E^2FU&#emb#?%75ObHWb3z@ zkx=|10(vbbQG3|vgaJ7F-7fll1r2oVQjZ9AOuNj&$7FHP$@6QgpI&zdVSu4sbkJj= z%PJ$|r>ga1_i_Oz;A)vD3{qnenPA&dJpm_&B-g{4`D62oCeO5tLr%xZC9KU+7b~(q zh%6<#>zs!=Zfi}WLa>PE{y}xWXa<1I$!c@b!IZwFXsj{_DpiDG1P~~fzS$`@N{$|r z1axhQ`EN-W7GjWbEO$3obF{kcCo&=^ zSjW=q-oCjU^Uv4GU?C#rJ43YI9LMBhKU7i0qC7Igfys!!-NyLE0)OJAAF4iA(5y%D zj?iX;{I~nKnKFsPZK(G60-{Ev#{j-}d;6a6x49B_pAgc1&W?^pzYr6M4P9;dphYfv zL&}1qw511um54c&3Bq(k#;=~;Q?ea<=LBHU7v|2p9be<5fi_f^ED*fgBS2jJW4tm{ z{Wg$?z&p}9+hDgrhK#gEZ6_X%(B~}SL@bylgX3dawo2cWzl$mjDfgPaAH~}y z^(gfL0{eaRDg%wMLz)+#63J@~M^!=HRvuB!b|(xm=RU91HLumtsP^T#H~GT7*4YE3 zN`(%)VpCN%0k26Z5=-^D8R>_crf>j=C*2&z-L|Hdy>!U1j z1b|yS7z>SyZM#4wxZv8Y(Nq~FeWH;%%5FYgPR9!K&$5yRxf$Z0*RjA7j~a%LJAu19 z!zvz(cetjWO=eR{63buDNeiY8Zg{ zam&qf5VKK6v|TvkMb7Jc&EH;k2ca+iIZj?-d_u$9{8q~wM*{OmWtX5kSYFPnHE zmCn=2;~+CIahJEH1xML9Vl2RySABg-fbz58A`ZpYg_nI}(tyPzjte2zHA2l>W&HF;_u1HCZFaDftb z8O)L>aQby!inA>Lmiv{$YKJripRt!3TZN6e)5VZ=AN(@>=pF{DU6GF%VY!MwQe?xY zf|(1Tg2jrs<*IijCcfYnM(X2eeI~-P(|1Ku>g+F|Q)%=Oz#aWx~pb050}6!?LKmdGKVw0tFK%3yIak0e8Cgx^cF3GVj_Ou*dW2R^f4s_ z_9NQd!Z5NxW3mn}o;)fO)T0O)H+;u5%*tFgO8QQw1q1$227S*r6xSz&f$G+cVEdh- zGa6ghNtgD2db3YZy%T)1F%-}Z2T3q!wVktGc1I~L7`5{61L(g$GYpY5Lnoi!Wh!XA zKmm0b^=-=la@nf#ct?Un2#ZQX)wRZC)E}C2(#;*feV)eG;+#RNU!$-1IiRj~`Y3;y zdng?lT5tbm=E;hjVatT~nC6bY!b&hwhci5+&pVBhI;vXT>UI)8z2UIZ^zsay@e4>&A9oZ_ zYQ`jYLzJ1&LO%2t)51`hzPK*0e0(OG*PE=4UF z^>&is4W|uSD9Pb;Q$2@Y#*}cvC2@Ad#f!mfLyCiX5|V!cd+o#R^kAMg zvjg+YoQX?>AL79+>6JsBzAPpcc%pWl+&xyUYFk`)6dheC>@a<-=W*Ol0`eQde~NIjFVL0wG3n?eG5cbh6sGfA1m^dmY}N!Gu^%g0o+2*6Yx zZG)T(ZYi4I7jsBS55CX-;`ZKr@sVNrsOJt@<_-LPQy1=Xwu0tKhmZItk(qCHh@F4T zcDGO=bkab52c10ZenR9-KJrf2YU_!ds}nIbxYy|mHmI)ptx*CIde|HDYW304Qkt@U zsh4c2sKF`0XFA-;!Z5hroinXJ@Pz5!%tVkejY)NzDfR#-u(}l5D;ms%M|!2(D=@L^ z7;;m3V{6q;DECqnqdw+_?2_Ty5!m-#gVx4retMNtWzCLY!llN<%iI^g_-vrDbi2M= zcH!W-0#$`dfj{%G#BAGvU~iy?f}CYC8hWLhAISC7kP02KU2EeaL+2EGZeoRqb$ zG!kc5yhz?$rLl@eHZ>;S6ps5wHR2T4v<&#BhZEq;lH$~obn z9TS1l1V5~1kmX0^OeXk2?752d)Ao)+Odr)VcnPDEC1emJBs^UoOB3fxn;xoIs5j%r z=4Ir2|83A^R$YuAgac6VEi#2{y)f?r|5Dh`HfUNv>T8qx+Db&uRUM0clzJNez1*1H zC%$Oe9ZYsfEi{_Pb^dJkqs~>j#+4k1uLYO4c5mWQ?}1*(DLetad;4S=J0dB&V$^7b z3kENpk(;B>?J1~r`C)&cGq>aA4XfdgM=%hP8S*Nxy=rJs4FYR*1LRIRttlJFmca3s*!4F#SK zTpQw9q|+-eR$&6+S+B?nW*q^IpBQpBXq1z>@85Y1Cp|5ho40 zSZEo={A1@Xq4*94#Rhcp1XxI(geN8n)RcB_(g3{@i~K!4U(McATJfDvF8F+td8Haw zVP^$=;(2)*2%dukih)-Eazz*+h@6y%vW45Q8?Wg&4Z)_8JFXNsqxM~V^tF1ixs1=E z9N=M2IFq_qL=Og4;6GyrsZaDPS?CSmuu&~d^}J5%1L2+vT#7MG+GzP&@N5yiO@pU( z!}=J@ruAv;HVp`(TUZsq!1c6SUq7y_L@4sd7kVFaR9`+vm958(JJZd^!WhVF7YpmA z*{A*UA(&+21)sWnApBjsRO)=bh}jQr_N5YBH48SjnGcc?6>=UbJ-gQ;s|>1>(^xhh z(aOEv4;xj<4}LRhBvf+775t4AVSg2^uGMpsv95KytjkuXD08p|A({tW-mRhP%57z#*~E0a!EaDKJMuaMTy*V=F4lO$aWZZ^ zUaWSBOZg#pPIwOWLT{U30?P?3g_N6Cc1zs>^u!UJ@s$4~E}Q;S}NITvV@ zC)!9OilnU65%|K3;3sySAwxIdSp2-k$&;FjblbC0CsIBFJ;qvtLR(Ky*F2-`Za{LZ zf)vPz-ai8<34<%OVYgCGcaM+8!dpS>t*WUKAaMxzbhGZv6&C$$hEl7YCmffWmVSDV zMyla>cKaHGgccU^3RF@+xHrMI7_3Vod_F-R>6(O=@*6v;QN9RXajxR`BfOWkM=nT2 ze8styof0I$!BHhDmE9r`jR)Y6u+cN&ST^Y{ma%v@oN|J4DrO7*81CGN;7GBD94`P* zo5+Y+JL^WA=QQ`eV4GDT9i5iw{ZQHHD*7B^igkZ%wKg51v`I&nFyH7s9FI2o%6+yh zsw5i!pz#`ah+I9S9dM1Y$;0e3qb{ldFFw+O)st}k_PQ)?Kq%pIjN{Y2Tr5q#616j8 z5&nZcbjtZdMVnWh!z(ya?<1EnZOC!roMZu#AhnL|qa)y2eEpUk0udIiRzg>-r zqJ`>)MV+4-pc)B#X)W!CihDWyewB5(6B3*g@kPPM8(cxKe~kq<(^ zH~zHdmFh81S-RYCxlw*0;Ylp|@Blzysd@wc zG{ddB-g>xz$61|IE1Z9stl$kB-kaCvs&#wQnzH%b4-?$Q4D9gXzk8_2_|fPH9O>eF z_4<@21e9!(*e}b2rk!l>W0K-UR!v8L&}gNtcg#2=;R*^ZD^bwOC+xlO<#h;xDnjFcN2bzQ$e<9B=9mub3kl!UcfrvKh3wv7=Jd85=Z1V!kq+ zx{``7wsHrxaQOyDXc(kgt*#mYWZzY=kx6G#Py2Kf&s^6{9d-0S{zIpM#!6Xjp0%bm{n55M-Grvkstvcc8CGFFUzhrL}ENQuKmuiK_jbklKg^HP~^D6Nb+$>pp zB1vCFu6L+ma*iT916b!Kzj7V!ZI^@cvSTbAHAp3zmA zBB=Zx@@2siPc5=X7TbU&YonCx=N@1f5{v8DHd#H*`+a>zdm8HS48-O|b^!G)qp zGlkKj)pS$rzEKQ93Gm>kCrLsGb_adOX0rpwk~PK)o+A3;RR)dAXGb_(2>yc*X`N3) zW{wu+A5QfVN)$%FG4U5To@=9g^flGHm^kpaA>}KQWwNsB#;`n!bb%NwM@HK)364a% z!R5o8JS@1+YpGlH(u2wgqdY zlRY6Z)+DP+qCHplG?yU)oqNOUa4jb2pju!xqiqZ{%TjrlADJxNJ=Luj1)YfBN%5DV zQ|N!Hyb4EXsc1it7JFwST^(REE(q$%kHr|O&q)bW9qbe5^==GRCstg093#W*VCZ#X zb=R}}_P;f{q{#H7K1!;LXEct~aUiYB(4UsbT9}UR=NSneam9K{< zxr=X&AD1K@rMwKB&x&dRD@?aS#z$vOnd;Uv`_nWL4@M$2=JD10ia2V?D+g^pb}ERA zgOur@J6|HZk_*%3_O+sx|33~d5D0lA$)BEC^~B?1m<595($eRI zO54U`0h@SBzVW{2sI*a?1jbHI=peE{!$Hwwr4Lz>DF4SjR92#M@iw%_;b#v@BbD#%Y%@H77|UBCczi`9D(yftA9$=lqr7}FIL{w_+b+~B`eW|L!qs` zDV%+G_sp6J4E+dEED;B#5P&8rAHo$H#X0Df| z%<*6-jRgW1bXQ=~!Q;PfcC+|6OdcabIbhMV(*2Uo|HSX_g%k_2;zjXh*S0eZzMf@z zF&N5d5#2N4cKdLngz*Z+H9k~YJ0A?!cSgh$cGdHSMKHr>AAq6T@5BE-R`(ynhX&}@ z$`J9-Gh1i|M`?Ol)aVZ$6Y{#&YTcf}z*75=2`XB&4lYOG{EgA)LX56)=p z$=RI98jYP8E_>d(X86C(E~&b*LXTI+^mP)0TyjG%Hyj8!eI|b`YeF37Vioo(RQf>@ zSa|$$fSOY44!By#%IM%~!eL4*3cP4{Brf>%a~kvW6FJ-hynl!N7j#P3^^W<|Qnyo! zc$wE4z{!*hAQe7rjm2CStjJF+<=qr&N+?QWKG|&QEcoxup1Ywin2)X!K-b3MXZtuX zs8(nghq+ZNC}kufn^3~FU&4&xq6UMAkvsA{WZfJ8$BSX>t@AHFxM#m7WWN|_k@QLy zJ>J0ZFrSJhJcXajLbpXoV@&p98^7>Vi7zlDdKI8Sz{m}?a#8bgGP6=rsCe8Zqlh9K z{*>7@9`l1cpZl=motO?Q>f5^Q%|5NWcB> zze)ej=4jv7vXmR5>km{SfEF=y)mlqq&it?}G_4u<(l@5X3gt?ln3>3x9WQ1U7`U3IRpJy8X`^M<#7>DzG8DO_< z|6b5O&G$6hhl)l7-Kq2k;PbY=7IK$wT>X#kfJ!0FN&b5=F>i3=-H{xlnNKHDB42_z z2-llEK1Ijk10cjKzNavLCK!<@v}tl_X@7BIq}%AvlJmcifP~V4iYva>Zdeoz;UKEa zrh>uy*&+BwF|@yoqKT;Sez~3) zW9ajqwYG#*H1@=;GyGbg)lf!6jZ_te53=gnO82Ke6#JiOLB>aCa%u;l^#K{05W9@U z%8$Q&kTqp0;Tj9P;w7Mkyk(jc#`NRDbk5)1=pL14SqkE(^E7MlhQ%YI`sf~f!K0=B zYTO<6XItvO>qv24`9Ar4%(1&BuqCwE2IJvS5+5XvJD0&6eOz&QQR2!{w+&lFqWErv z$dV_+nw^4<_l=iC7R^XL%rS(38vr43j{TH(7I8Up!zs>ty#9MW{VNL4nVs5K(d0LD zhG0;={gvE##p+!IwvnE;5hI~(XjqjO(lE0EUA2qWn4fr;V z^mIW0tVm7(vUgV{`+EFAq67LGA%MfpWTuw zL#Lkm0^E9OxPPnSCyz?R*!P(Amu)2&%k5ybgeE1Ks(fb+1E%1|$#|;wXuFuKFOybT z#99k(9LH(2VN2V^us4`Dsd1Tq^)}lU=lV*V6midmI65{oW}DcCcLY3CM4_Q^Pz>OL zMTg+?UY~w`@ciG|^KQs?=a{a@>G&(`7G&a_lLPd{xa!(uc{5Ei*4f7dV;2c#P9GCj zDPTDYF&&t1B4anR`XK;~d>Qv6`2wh?MrSn3gamRC{w^*2 znH7XT*fnGLidVcFTWM`XbC1We(G_z!^$q5QcsA0=vAf-ah?qtqG zP&&|I63=NUGU`ttG6ezjmE45KGAu3&alNJ0=ABoTe z8VjfC8MJs}?<&E`(PZTYd|7Po9lE;!{G#{L>wf}}2<3*dO06!cJ_2|O|DZT{Jd(I- z)GdRrniG;#^{P zJr|$dz15vjG>YV0dQ&;#1`>3%I3((sMO%t&1tq#Y`ZP5A9bH)3C4H8P1Mc}-;hJ3zn!c+RVCu!l2L5fSpF4UV|5T-Zx0pJs*gWvxnYu~f6@GKZ{8-LIUrkKDX2%K8tBu& zhseL^L~Q>Rq@&ds`+lR#u~xLAwQ}ca$zVeaT-sKy<ReXKf#f)kaNi zN2V844NWHjOPljDn|}yIQ@N`c3JTt9_LmH(bS$`%&!PH}^s$LMmFm5iE9&2%II550 z^ROl{`y1PwsoMAC+JRHMJ{UZOJGW%thPq{0|ru-77*qIQ0NdYsm=&uuZ$&`SX z$Pt@d`&_r^bJW;$G(lzy8JxE?XhELK0Y6ifgcs%wE2?*9m*Bfw#BYv)M!N$Rqzb0q~z}55UL+!&&r06(pAs*tVx?kgp0QMl8U)p$E3@S9#jVq~|u84Bv?) zR5h0wq8X@xfG))nLSKtz+{Ng2)XeX+jEctCw37M9h`x0SYW>&+mJb=FU(5*2)Viya3OtSR9 z5~LIa^+ME%y8{4DT%TZY3B?d5{1nx4t#Dy*XxecGKYhS-6cLTcEOz^b9~bB12gQtE zO~FZY$omvwtcizbT2_2ba98vwAL|L_F%T0p0CIT7ht6CqL^vBBhA}o4uJCqtDQN8E ze9ipo&5Q-qOkZD4AjMJL-|=?TKpBndZJp5-AS_XJIpTiw^RDqRk!HwfRqfRXtzC4lRh9Q~2#)#_$@@0$9n`uZB`yBh6 z`yg8B1JCF7?k!6nF3}fC`%UMqYIBW8zVm&$XbzI;2aPA^&}(h6*7N0#{l(@7LVVL| zcbSNjj`6Zach+x+UIi6+P|&x$5c_Z)-AhsFpb zFQxbblUcnkqLsuR_j$S|bW>dRtSHGF18pB&s?vL$zmifIq!;eyB+;W9pn``{7?3k)k08le9)Ah*eVyDf{^%Jfmu;s|z0V1Y-C60hxKvYv z)x5#mEL2qX5FM>OWlT-;@VLD0omOh*;9? z&A{Z~jZ2Y!8(9GLtueRmc8y4#No3db7}D`W`LA=rme_yO`xx}QmI)@YZ++QFVm2vX zl@o5EFHiuiEKc}>yp@-Z>DHBym#tG#(-Dg{Jvi29PPwsohdOm<$Bj>D& z1v;_O{a6C@^$RBLJuq~t8Vd}~V-{=rt9B1GRt~@d-W^gXj#apFn`@RHOfkLvHj%l98P2X0}BQh46JstrKu7l zOWcF6-$@3s3cCsmAB{EsX8p4Gug<9y1IiQ_+Ehv!5-uLkuyFkR5f%u+AjV!By)`__ z@RexXJnwTJo2(=s>xm^+3$Nx_5QPRhNR~QUrmu)fdrjR%YbZ*cy`xZ7PYzBKw+KX`Rl@?X>l31t=aV%E{4 z_*%{_G>pYgli|lZj(!_VP|Q;`0UTC&8FrU-*h`HL84*50&8q3TyiCOs$4v5NhF_=;N6^As&DWsWR`OS34Myoq=2_otzIK^$43xvW{^#8t zcQr3d*n@;C-T@e#M%o;gxVSGH=VRX@Ps3%%N)cd$9WoH29{eWw&dLP5`ao@hUJ82t zJelD{^=*==46<*S2mG{VT>!|e*GEn=-+Lixp6#U%D{q)pu|%dkhvB7zu&t;Rf?72L zOSH(0#HAmL;&m4ON9D_J+z!OrkJ3`!H~%Lu#s?9j{Y`nL)M(m6upfaScP#_ih^qEG zz=1hof~-f`Aqlt%|g-q8{>FSF2o+2>v9Eui3JYp|j#v7%M0 zZ1dH~`4KVc>%)edwJ|LcOL~3HAh)&sM!SQ)d>xTdIkjX2w5AAk4{-!&c^ z%hwS*^beN)%TOlt6*q0Ur}((6@eqm<)4oL7jlp&M#a3S4jO>y5UuOZ*>7PL8HU^GQ z2hioOcR0+2nXi=VerWV-XyLQma&0t4vnQZSKU7e~GM2rY0G$G4S7iyT_jYlw+Bo$c6-L=_5+*U#_dU*cM9 zEekXxSNe}s(O(;^Z2smIxQ2z`h2UyJHYTyGhPodYPNSdT1d;FyB_iIIF2%gqBH2^r z-KQJ|Dc>?`H$J}SE-k6~cpo==v7`>0T}OC)IU+c-iH*^;Ut&WfTCOL-xok1C^=I9* z)O?x~qf~LDekMX&6*$kd+q_f{ zynA-gs^pO`ZCM8}#x_R<6`A{d3>)k1Klr%*v()=art zA*gy!ZCnU;$^!5sSGypE2>-6V$0hTVIn#1K%Ac9Ra-ToJzOF~vfUo@W==+S6`>cTT z+${F?^%YW2+uX?X;L+`i!DmZ}LlY?V<)k=>k3oKmBMUQ7l6KAv?Nj><*{axIf}e-*cc#!x^8Bh*P)$NB7+@5Vh3 z-1x88N<}xx!T=4}2;XwXq^BzO0*6fUH|JUy*YTRXLBLsGm zE#y7gyLNct@$-MLHhJ7fC^eZ+Hli;6mOUHQeZfbEvDku6g<-nst+^W^ZhHk=f z>>|q0UaV17IIPTME#Z73KE6g*ihz&c4YoobIwpHRBis-ne?Nq7BIMl1DOQa(k70^g zqa)aVREtgBm_34WDoDxnMGOV~(B9ZkS@4S`{N2j!73< zo)-nvP{zlek))v>mGpk<8@mV1FJP84r;APrdUo%LEvgd|(}vy}T5(ZG@*90rc+x!k z72|fc;pE<|88?o}$$^)y6#a7wDNi|JGr3|j-yy1*3dHQ`7AqkqTjszfH z0qJ<*0aM0CaLf*&p^fzm2W+mnb4}W=AJd({ztsC5Qm2AXLrc#sxXWU(Zc4EXhSCzq z=PR>^L>lhmBS={N3dt5Ub`rAFx+mQ8bk7>i1vd+J&&*woo-bq)*JIFzBJc%swn_5` z9u{klMG8U3Jq`l_GZZfu>@E=Ua)m$2TJkKt&H0_rveo|LxBuy&zCdX{s8uq4kg!$M zcku=1t`B_Yk=N2p&iuOxrb^d4_{K0&-T_m3Jp{B)@uGrYh9NUqIXdEg3yLE1Mo&QW z>Wk-V*#Gm`K;FPrZp&J?LlmY4PMqrQ@r5lwlnW3eIZb<=Y=^=*{P+FRyyTAL{`+JY$efIvo zU;N|dy6<(Z^IYpZ*J6p;#f>mt0-_|sss(iNe-H<*MbWn*)0dv;S*jkgTkjP;=3L6x z#YZp)d{XDJ9C@PO&iDWg{_kf!#qyYoa)G^DkLsXi@a~J_Wfbqq13n9zT8g9c&VX!8 z$8>P8PL*umCgLoEnLqKCkLBTHa57*@h|tqmDO{Z4AA*L?QOke?6E4{--$v^|`ficy zyO`+NC&d{i3W}xGChnUlPm?*-{*i_Mm612Lfrq=UGe}`ofW{CFki)X4KTZBNJCKU! znNa1(0k4qV%Q`_E5)G9rRVt@2l4E!R)C%8zQ3HhAJaG2)?SDpt2J(X*3MF{mEv1@Z z_4TxW@j?`<5U!;)Hqk0z@bpDe7%@H8s|N$Wn$uUaBj{@36n-q!{1d!Te6;4ObWU|?K zn6r=y&3``WWfsrhk7B3f0qs3{C5t+}`TF&>78sUiDWCa6rauhaS{_NM^<7bSOubi} zDo$SRcc+rqTLMKaJD$Em^#NtA^g*ajn$Ea6wTp8&EW{}4B{!?DB)zplt?F%5i|rs7l~qg%_K;aQUCpN3c9vDrRtQI_d@CR}^#9ac^0i z;-%G+G5Cf@PYhS}u(+z?b2#{y{`8!wN*F%ZRvQ#o@Bgn9hQPu2SkL0~* z|6`ot-*M~z<;Sq^by?9e!i#C-&L>k}j&zAx*V>?|yx|1>r6vS!7r$B!AbJi%S=UC4 zw0@q(S6RBZTG6E}6DlQZ^eEnR#VqQ*Dv;GnS=$i(nb_WEAC;Ua7d!9t4GnKc5oix$D#_3 zPCesP`!8iwQk|X>!c;-PoGwG!pB@_p3Ckva{qZ0(J@V zvuUJYJ^rl3-ObTY#ba$i4$kF}l0z5kf#OzPkc+ICJn`!hACx6C`rF)M{NLu54}UR& zWln?HK=1mQ4YZeX_Pd_rQ{`mAS@3tf0k>-=VXcoM1j$!F&&Wa6m(Qe6)Mt)r;q+pL z=Y1`Bs_-kKhg!x*;+v78dp^k6Ac9*+&Wx!Wz7mfl0zXiv`R8$f(|r`h0=@@sEn8&N zd7|aXW8S__MXw_H?G9y~SOV#!K5>ojj7q!sabaytPP*XS1UkkyGrDGYi37~`Jb0V# zyEIGsF1kQP7Vn4G@H}rMd3G1-_YtnS?$O=wpE>@o`z9#j7KqytQ?=(K-X;#mJJo!H zZKj(Qkr$C(lp^|}PY&Zs_~O|9JTqI?4ozI|QwWZpzTjhOhwlF(K2jSYQ-+3en8B)L zlZqI3^!5%u^l) z=3hGT{`QAjD>>2}Z;JY)v`w=WiuznNR&+J+>SF=a4&Sct>EkOad$7&FTVJ|VQrlNC!qa*eM`VKaCY#tf*{{0!f2Ff<~quP zu3$gTg#VrTrC~U|iAf(;X!Bvoj4~54b|mQqB$BFRJwz+CHeug&klV0YowgcLe81gV z|0{r!e<3=Qz8A%v_Jxq>8h{Vl1^NnGeImHZMiiz#x3&(SM7sW?`1se!FNHtS$U7l* z$=N-2BV>QH5Pwg}zRfIo_yva{oFL*m*zqc6iP`=W`dh5Y69m*0#wVk%%3S|4nN_B` zwo;W${9M*#pwc5Tm_Rd}c!EHaNh9R`rf0TM#O=w5J@X~dqu;e8gJ3L}$#Wvx|K<{O zfxli~{%4#451gJ_W!nFk6UR7Sk`CmKW)90grH~LOvl_@9RFq-m_3}M9lN+FLv>sPNZ)>! z@SDH(1c2_;Feu``pp^_z&i9#+tIcGW;$+^d2%UBXOi_QLX5z87+WwdRNBR38=0T<- zZ1#}0@SGr~avYDy3fB)g{&6~52v$Z)h-_6(!}Sw@8bx$vCs+)9d&FLrx5QtOYe8u*oCQUpNSr@pv5}_lDbvn!!T$t7M{6qyNUoH!lZef$~%{KrF zt_3}+`Ns;bzmt^#kgOQp``!WkQb(9Xf)T_mtw4}^`&RAJu({CZbzX^VL!^@Hq;g&} zRRN?-NjJWnul`zb1V?s>VWyRJTbolVRHwU(yDX1Oa^uJf#ca4*U3pp=SJKaJf^|9o3_e*wPG9n#aT4>wy0`RLguoAHR zmo*H53x{V9C}#UuZv^ABFBN3_DB5NcG^8qeQR&%TL+ucUo_V~K7viW_ulD~evdczt zuuo*Vz=HL-ijf=I$733S6eS6H{ZQJUVBP16y7-FX#r?7E@nIB3ir`R%hL-?q!r{z5 zSVE9oJeKqP&r~tei$eyLtIj|R9nUW@*Y;QHt@J6#K755zP*vR->cY*ekx_lg5lN=& z4KWEJkaPo}pivfCe!-`8PX^kig%E;W zgoade9*4S|EgULKcaq(kAC!vmLC{$Y>yV4) z4`RKzZ|odds8s{^OsW$3P$Lh4ol1;9IEpyrEHr&E-q*ay+MveFyFDo{P-Kuv0?3Ig zVo&D&Aujxjd@}+KY@kbxiq+J?KOp2~*X1IC3P*cG_;pn$BAH(us%OZ{v3$xaiJ%{Q z$NE@RaoTaH6ckXBsLTXr{B9YcV>uZ|Cj*l3!qusPXP3T?b4TD3v|jM>TkD7TQRTqF zQB0pI$A+d)^ciPt=zM!#H~#0VCQkfu+N4p9c04lA^-kZ?i^08qeT`pTqsz}%bIyPHzi`_Tm zT`XZcPdl{?p~FWw$;qvgv5i0hP3nGyKBg1lGr>Pc2n+yZpe zf`0UO)gL9?dL1x(o^7LtxZI5^&+B^m%+-5t_S&Q@g-p)(l75d+Kk8~c4b?5n3_6c) zJ`ZM{4n8Y5y;XeCb9rfFg*1SvRLhgH1rxY>DejWAw=f4^lNs^GKsDj6Z^IP@1TrbB z-?ewgFw8RF`x^d~#~WhLdd;Qfwt_3wRNlI(?mZR(<)_DAjim46WYLtN)Qii6uWhVE zyr``p?~s<{$Y2evUx|>sIkMU)Da`X0BmbSS#pKu8SP9xXx-50Zj0#1^9+;ZmOkXon zypdU_K_E}tn<^?rT;E)|jU$|R>D(E&7b*f>oMIa-OFw@jk!v6|upQ0DBrG^3Am43o ztj|aYT~D*;-Mdpi{KalW~%doj6@-RX;RsF$h@B$m1uLL>Q z45(k&48+`SrAAfWsNd>AcCq(VEP8kS0tcKn^6m*OSa_ed(D5dx9PhEt+vwk&+Ld}6 z=yT?njV?|&2wCFuK={85dtw>3B0{P$6jP_vf1-)#-b!;0Dgm!=Cnz3P_ z5DrUbix9F?vo}!e?iTFjEp7&yc%CAXA~ zIMxFR9%emZw)y)?(U&n*X+P%7zBqme&NXklbv#GCnnpdOuwi-=-kVk-G4N(6b#(Wt z_4J$=V$TSBfK32P#`yS;jTVlxR0WmO=(dgjpuN1Lb0CJ?=Y7^YzCnFu9y_A};(Ifc zo))-!y-HM3ZCv3sUsdllgms+2uic)pBGP-#yGOkrm0`c6)y ze$vDEeK&Xfrl+Pw7tA@RR{jtbR+;Y{(5DGn>(vB_9?z48L=)Cu-~a8iSHV={YdzbG z5yHNivnV)aNprknu3Ifi99thfU*XJ_v zs9qGHEw1hshv~_HNa4eIs&@Y)1!(&1`UE4RG9zNA4R{sN0RQ|RkxfwQoJkEP6NP+w4bK%|uwe?C^%`xPc~IZ- z3-jS~iwuhGWrpkTH;R&fiK4{TGO2g6tqt^h@rSahf(ft{XjSQ9UO%AP#2f7Z^5^0_ z#4R=(g8_KR<$x&5Q8;IhqEKbIPT)xHB8Ei%c&ywVr!^dn8N5bv9W$z0BD~+hKw(?O zh-MSOpBG3j!9P(M7kOA32}hh+;c#v$LwhFJ^Qe302$Os03((7mo|Wac$1HC+?iw*F4<2>9FbqNnD)*y{$C%#{NQ;K-Z0 z?==Z|CiPDZro5)uf}_2-iUB{|d+!_};td_3+6yN&%khzhF$8Hn67Uw#RoCLh7Wl?Y z#7r{G3y8%7@k<%|+9hrbp-r%CArsU{l9>S0S;C>UtkQ8YXAOrh?40HD+iEZT$T}f} zINL>u2ToQILvwVc)=E++(QW>HgT`-tF z$m6}}$k-GlBoaC#X8XkVVD5cin}h#*Q$h8+kC?d#AVblLhp8e3GInZ~EFAkDCcSZO zTCbjxyrvU7QfIBAswq(R-@To?naSy-RR5J8uzuheuN&CadJ*DW(=G2OX?NCMxL38_ zBh~%Z&rfBnVC#G=hqIdQYpZ~<<%*W$Ex0qj0I713(sD8CfC=rbP`j+A`Tez)^QvP# zNj)_qaADK&G~EzW=&+0WY4X-d5cWEi*y-0`F{XG5`C3TgJ@=K{FImBb#fzDH)OQT% zG-!iPk|CRFnLPSYYLk!n^`ZH+4DYPXi^9_@@NNxuW}{x*b&TWP$vjcj-ES-Fx?jJd zsu1{viZ7&YU_NogyZsB|-D|_-*1tPu18y3pQx~rMKpvGVlwmc}L02`mfLGRE?Z#0r z?gOI4Q2d&;K9b_NcN_iKEQY(~8b{Ewz1?w9!WdEA6@yjdsOk&($CgI~7^I)_OX|)DI#ny@mP>YO z*Pii+-vN_Vm`KK%Q|z)13R9QX;NC$h*J)p8wKw(VH}}(=yXfm~W0O_Y+VPiKEelrtFKpku^7>YZ~Z+rCz1gfs-xMRF$ zg{DCR!$={p;LT+r-5D}KJV;fyLy5NeM)!1ar($)-jwV+0POOg(kFHtbi|PBVwrN`V zK#5|m7kk$aX3_y8jXa%-RqXr`OOr2VAhB*_wiwbkX8shB{#&9xP1P&V-evb(pT#iV zzsE@bY2bvW15=&W+o!b#ST_fp3IT$4EWUx)Fge9MbKe8fQo~-;!?~3Uk=5}piW_7Sq{a6A%gX5F z=~rk7o~rNl?ExlfpqCk)OM*}MCM>;GZC+?j-9$Y9bgxaIfqz$u_6d5ovHbFa#-iE4wZwj(&8`NE|zlx0hlnV^9>yr_}Z%!*|DKDzty~QCz}K(cH^Z#XRq=bmk*cJ@OjS#d)92zwZw4? zTH<;f>aG7m_Cups&Afw64=HOjRwfN!{eesU9KQQQ(I=Po7{sM#-~`^mNg!RyYoS%T zWz&sq3AiAvyKL;1n}IR-2L+DuB8#q*&Z!qnKkfK*ZS~PL>h^A8OAX$hA!GAcbb+3i zXUFPx4Vs&eOC(G%&4%v6mfhjb`s@bP_>4&@-4~dME>@qbTDwHr5gjh&rCV!b5l|s$d|{alW{NJ&USgl z9;$7kdyFfd^xj@yRon7D9^4c0`I%YSYd&2e7l=Oh>|$n>?905o66M|?r31`yzN-vxv4E~-i1m$qeM(pN zLRS-&)&Go6{etZbx|re5e<1@DH4|N&L1&7%vNmY${)tHFBdNVVeRNeqQa*`5C?3c% zEn<5MrXPnfD#xzueLeQC`y=YRQlX9%h-nd7s$IEPh3PO?iAz5GGa*GgaC`Gwbq8*AvP)7KOVE?Lx^Ac$45#u5iI|hy0pgxJeV$K~ieB>3p@YoMqzUp|O~Y`u}+eMfh-(h00V zGtJTf2VrrHG6uC~lS@DfR1;gYitHmc5MVs2996o&=P|13&tte7C;Yr6v3>3;0{vo4 zJ?CBXx1&lZG*0dZsQMk>P6T87*9@4iv(5xZkGQYiL{XbHs)r5>sRyXSe+c;ZV^#!O zhfPrQvC(wbW&5bCAtuvtH#q$(S1qE$(|0E*;ms1lINzAZm!WCmMJm`p;1>Xa|2;fa zs7touSTG`Nuld%F!G!DEMst2#pAq=aSsGKOts9&bTOx3C2I4mTW5w`C>C(tmow!JM z4gbj?sRVrEOj-teg+Y`Ovhn5phBJO_ba_Cv{3CygqFyOluc$fi9p%KLf5kr-c5V`3 zi+}$XOBUQI)-3z=spWY_S+%z(8H2px_r(aQ(qIC+=#}K-z@A{_CNA!C&DV$kN^!Zd z&>dpPvm+~}xKAtK91>DnkWx1&Hw%nIr!`5&DlN+j%X`a74Dl&|XG`*Efv4OB;2R-i}2a;PaVoVEhmnWWgM{CJ7m+$Nv!FYwzge7 z>PpkL8qeD+J|UW}`3!^abK|*-pE7z^=INL`(tUcX8VsNoyluY@RqB3J9{*mimkfx% zIZTp?R@c!)DL9t`x=v?ubz7@fV8HOAcWC9#)O<|K%|RLleRpeBT%p#Fkj^G`zyV_& ztCBZ7(5ESZAFRmyUB%wo1Vmo;9`s$^3gGS$6J5&rT6=lO*rwU> z0z?M~8^4Tay8m_o=8OEiRlNycIgHT>N3GE%=$gnSgYw=D$#9F2t(9`~y#JpX7~7>x z=J5JL_?UwzI0$N7H@99Y!i$&Ru(!7STV3=n1%GNFz$Wg3! z`ps2@y*jZjDy#NxZU(IYmSf;GqVC_O4}Fe^68|dH=D{OzfO|U8xyRBr9wzDU+cZHr zJ^~R{fy&MOJCbjI0qu@f+sa! zFa^rJ%wXeXY4nCnK$FKVvz+B}w(*=5zxK(eob{!Uw(GFt+1qQEBUW0qsm!CYpPk%# zpQGVux%ZaD5Wfn!Z`k*w?ia(`g^rY{es)nq58kl#r;*L$+5yNO6|e(nyqN^r9q7C} zsHsK|dSTKR1`~`Dba82@Tw;ZJC72~b74C!l@s`%AmD&1T48fTfuH;BCCZZoq!fy|CaaqiGl9<+2ne3$*X`#8F2} z9Zhv85kOo0eW~}g)T^8Wrh%G7{9bU_ZaofA0S6tzHE!>T_DEdZ6Fb}JZN?OH!d{!= z&+<)8+K_YuDeqc*zt@uw6x1!lRij+74g8*zl{q;il^;>nSwW!6q0Po)03^}dG66oZ z0_mzdtVwWSZ{LnT4wWF(iw}H&4vGGayp(9VF<;%bJ+b}?yK-`qb&+J=6F-MipYbQb?z2$w1$1wK178Dv?`ewzB39XSNUb~bdJ zH~ko~(0pfy37m1#m*maxxdxaabCM#dvgFZ56DH>m^4*T8Gb(5EDIH3Sb>*n%wK-Csd3ASb{OhcX4<+^?jJyc?Xf^13l6*lq zb==hx9kcg5)${n_c9pfBf~%8>_b+YC0XA$j79CoP#CSKV6}V``o+>2e}oZ0~D)H14l2 zn6$mWbqR+{vqojowaxB)WLi=642SmQp0)Os-|mdJjz{X%cKNM^F>uY5YqEha6v;_ys1bdLrUW=nO10*0@Pe;vtTcyGHbWNq{?Ljn zQ5%p#RAZyhSX%}@U*6pI4ZT$4Q(rjESy>N^4GhijM_0rn^>43BFe9qkntz6E-dfj7 zdiw_)0SBP+M4~bSo1PJ=i?9#RymxaME)%JFvO2TaOOl5Cp=ToQ{-W&!X*H9fzL4X; zws+DJG?Qws@W+wv$r-E1T&`W<7Qkj;tTWrb$1LDKWAT|V;Nyz$%U(Z7Y4y)_UYZD^ z9ZY)*G~9OAlxr-Ghz4usw!I|Z{VrAXtM$?_z_lJM1TcrKtNV;Ngy3ZM-|b(s8#!|) zWy_A!tFLih*&{-q3c7MRqK4{>C2?7^f;tFQEFP}34t0Wcz(FMm8r>8Nf1EvAGQZmh zc~8vk9|FtAW0-_*Y?BjyH#rCNe5N}|Ua={`yk8qiW0BI25GAXVT~c<_!KKRH=J>^l zM^)h4ZcS*3ONE_LCunRJ^9@A4>L=oU*VaJ5Tif2)50zsrE8SzDeLviYiQ?pir=OxE zoU8e4wq89Lz3KyYo49)Y_HXOgGrDngL{;)HM8RX#AgYx1DC%$kBbrJYJR;>o_k6Md zv(yK=k5JU};AAFfkQqt=SO#uEmC|4-q zzTC#6T8(5bJ`3flEV|$u6Q{O&E_%j`2p~emg7G=#Frf}_51XLA(hgoQx2k$6D)i^3 zi$$2gIvwOwM$I4cKD2t<0h5>%{yjsBs>EsDI$=KV8#MX`0bG5)i>)YV&uE*}hZdpY zX1jtO{oVJj0mHlcjv2|8H$#Gy)gkNZZ4E8g?MKsAcjI@XttUc(J%WbJUnRkjTwB&` zcvO`0cgCj`Q^&fbB|HoBz{{xjI(Ew3CB9>u+rIKt@Frjz7ry~>vwSMGlv4ydf7hUZ z@U)zTHQW!x44<)5Yc1p|q3HL5I_p|l+0XRdF}}+}GbIk(Tm0V|mq^;kzy8wE>ieD8 zmZ{gP84^uHWR$3RA-nJgU^Uj18H;<}%P-V22%RU__Km5z`aHy3PP)iASd~R+H|XWM zA|K=7=Uh`Y2KA5czWc$^XUqc59d3-YY7EVq{@z0jVOv=nOpDYgD7mWdr!6bxf@y&C z`Cx-}Ny}G!KPPR(=T9dVoVqAX__;a#ds8T5`gmWp8?eIQH^w`))m4q*_SCpG1zKgx zHjli^5!h*a_rRUWPksfxL^D$Dx34 zKB>YRZF_AgOjwgNIq>%#U&CIMm@NN&RdbbI0v}0YgVmFjYG*Ucd3*S#b1g1hI)a~F zalLm>eMtFbfNxDcmF{ttjD7|bvyjhU^fm*yaEFuxbRujO`v*Tu63XB4t>;pqP?jhs&{v4 z=~(e+N+qBi1VQCz$})+%{mmxH!9dxlt0C->>_SdQS;QyU?ECD-{WRue+#zK4s&7bY z<;UOmxM@lNMp!(^SMgkz8J6xn&f+~xuZub#)2D=mAy0PvynI?UBR0?GQ`2>LvKKbb z+toPN&{yL`mtt27!Z)t+Wky=$4Eh4*I85DbQM3GD>;zAGA z_ls}e7ExG|$hsve>u7dYU~ztxf3*r)0F^b{E9T3~5r8}CEl1o+f(e|433wctSoOVO z?|2{YA~boWLOEXVFMwe$QAOK`_lG<3HTsfX4aeVT_|dOD4tS~-WJ(dP3mY`)ungqo4JEs7}N zv!dSV?}15wZo04U9TQBu{`u{yW~qyNDU%#$*LvgAz!3gKEt?d=^W7C+E@EXv_=++% zi6ibiS2R%16SD4H5MNz-CckESzW=A(!Q8Swb~Jf~+^X&FCiZORqF2)FsmCaDSQTl7 zN(HwrYTB}we45D9jL*A*qf2_wDcc7YzQnF`xxep}%KLk}Bk9+~%znvKjBvsC;zCGX z!urR@RlrsT`LHLcIe*MNOTb2;l{N0qV=~2Wr~oHD6q}3X z$YQglf~J=2HUb<1?*gGFx`gv_zVWw9KPk-gm$~zu# z)-79dvhpsRqN%6pkUUV`t~z3fLEOiHkwu-CSy*30i9YZKSWC+#@@~-chTH^dd_Rv; z6lceNxSBIUvn=#c6#NH}Ir|49=H2H8FsG&F`>ly?tNO*HNOkeq?5kt)UmqaLj!GP8a?O z$v(sGy@-If$F?%S2%bFu@U?dM*I4fMq`sMjbK5F%pl{VMN|1hJ3dixP=Ihv4@yt;~ zvS=dK!&q@D`}-ru&XFs37Ymf9!N}Eq=3k&P!}==aO2B#31u9D?IYSkJAbIIeEK?Mj zz@1H&5e0EdMw-Fs4#2;FLQ2neUq8L9jB_~wxm1M0@5M$p(o)^|-Z6Xh)AS}}PKx!ZwSCF_ zNaE_;cZ{s7gy}ex12!VGbNrQE3QCHM4DwLA&^3+IuRe;V7P2_vh2x3olqdE7Tq6k4 z`o{EjRG@&?a<+{F)@7jozS#g8N|tjtOeSYxT8{0cu=#XJ(bnBM!1OA|!##wc%t5+o zxKWEYr#Ja~Xx%$8r6lD{MYoiWN`-TVd(m`&S=+}UrGtPScjGLe*<@aR9Qufmj8V3G zJz%8P)TNktMzfceyX9nX99=oU)B5f0lrS(6{edqDphou0b&#@GDT*&}rL31eC2^>A zpv4_SD_TLjTFvR4Vp`Sci8w&fFM?~HI|lp00M^Gtu3Q!&nScERdVgfa9jqR<-;_G7 zP00HZ-aegHA&n~HgpH`4m;V0z+F8X*ATXSWP83oc3{k)P=F_bk*pzZ}lh3f5HZSFH zHQ<7<99-bF4)o`9ms#(9>=0{7U6!sVnj+%+=(p#fS~S+9ITB^mciUu#nMLnMtTKPg zeVA^~-jfs}7EKg=E?W55;`Gq<)h_54&h6zE;h{Kb{pWzpIoHm>qYH%ReQ)-WgI#JQ z9|wY+cIBzxm@akY5`LcF29Ud={3Hoy7U_(1BtlAxim;i<^2I^*rWep+vO+~vmLwZ^ zbCj|?`Rs1_1?jY5m=uFMZ_?#NB3?dIdO~zOooKQhpJQ;`Bij1k4;m=%<8m+s@0)-p zVu1i~$SIP^!Ph;=dQLhgOgp&-x9u*MS!d zl;oH0KNEd$$swqtjr8Aa2M(9=dqc@FR%i3aZL`mP-rlZV?!8bV9w5>M48F^5%MO4I?BJe+>F<-+!O?o8ysVmPDgnicWW`r=L6@8hErqQRjiOuH+yw#{H_ zrVeAa)$y(nvyjW+2xu!AE7Ul%IkV+>S6B|rY#ivy`nP)PodNRO^*vD&pXRK0svvy) z#(^ifaen+@1plo1Zv|-R#p~D0eFwGqQL37{2Dlm2$|&JOY}Tq(O4R}RvGm)6{!ZLd z!^(AFI#hmAnBaxkyVo`1Y>5v@xz;|$4t+Ef;@EQZEBOg?u&L9|D=J(PlV+czw)p9a zGeqYQFbhouwf>A|`CBx-b7Am+7ge729J(5@{+-BLv#Np#d@{|v^p|v{r}}DQTauZQ zGQ?QF?(XkPIJ2C6@CgV1Rc|8s`R3xRJ5*o=tgVmw(MU0|UB-C=v=~Mjt}>5R>$d76 zBb8U?j{i0+g-}rU3oZnN%x*^*1N6l5(BRT{?<84LQK?}}K;dl5v(BJEXDE*x+H;Ux zfle}bbKB*3iOF6+6srjAf1{?}mgTiMgBu;Z$wGufr8@R%f&Ry|pwyM!sUQ1#;X6Fu zrEJPCBZ%N)VuPiHV}1Vs0>kIFl`auIM;*o}1(qBi81U~0kC_b}63ht84YXd2+NA!) zIR36Hu%SqsiFjuR+Or7moCYZuB;uNUCRzxL@fY2cNiJ}5=sdFc{**pcksl0GK?<}m zmA(7DkturDIzIE_3!LGX_#J!+qi>wq7fM8^E%zC6d6sO5F1D@}xa!kmZBUbH-32ID ziOPo}IwsIsOHv+)&-Kwz?h86X;dmhfsaQM8*IN_rzD=2e>~5zSho!R#T7n@8+XE%VOJ%?|LP0>H7zU^^U3dQ;iOBnF0w` zVPTuKP?W(TekYZGPf8{Y6B*LID>o6n)976O?NG_F?ZH`cAGK zsj*fDYml$PJIzn{lpTAsQJ=(K2Qytqn>Wao8$qLZLXyvelbBesaj1OSq3bcyjKTjx zH@7}{Z()U{CvlOW;`mof%pLLWEEITVtdbXeMNSozkViyT%3P~FFu(nI2TYB+m@(?R z;{>7O%eH&NL@Ju7MF!OW?F!nC^;WhYRUHL*=pG(PnfpeYy4`*0Lz|nx9(o)Tss90Pj)2}ZtZjN(R<0x13(nfJ>6xLi**?-{9#}W^ zD6CQ->Eex@q?_r&6;f;_CN{Qe_+;hluys_7!T=?$>$vpaW*S2@pFo!P^`e86^)^>q zfMS+6CNR({S<1`o-GXW+(0*XuJJOf?vb|{>aFC_CcF`;T=K80@2>c#UB(}Ee-O3qh z>4>9tu9rzOUvE2B>zL_HkZGgQ#&=-@)n899BRxowevD^mq8akSn<&kVP_OjMrkLqZ zQ3bRT`stUvr(Nd#+lmYOcW*FfN3D}Fydk0(m+2`bf{phVhpwd4U&16hyo#W@%!EqU z4Lg$N){K<2>3#-?Il309n7Q{SQI0Vu6gH*hi2<%YXD7ThQYQK8j!N^?*y-2Yb%S_} zwSisns(Uea(GqS~4w!ba!Tg}^;Ij0ok5a8^s%ddaBkmGxvcUX2fcJ{_QaX?0n!y`c zgd??g=y~rC@|y0>*Lh(~#Lyye@O(ZFgJ9P@`711}dzv~}vc%ox;g;>6rZqH4*dw$2 zexmPR8fu&~a^NUbWIqDM!RK$$4dc6m!*!UqaYL9< zwfOK>)fhU7P35H~m#Noquj8aOTgzl#XlLb)Ke@gDjM=tecbpsc=LFZZL(!S zz&pSYwP@ly+^`;~f((#$eXnwBNuc4_io4s>+6XicKoigLQWKFM-Q#8daK+!)!M zkYmtVhNA5-!YxjsW`dr+3!9`Q%@?U##K81vRJtaXTFB+ z#^M))+6n3DW$NFZwgr7Ix>KpF4c&E~lj5Ame&B+gPqKD$aGhtnM6^m4M@G_T7uK@z z4ia$nA|KpoT&l^YoL>@RcMrQK?@jx~{tgoIcis-tpk@>HUHnRTrAKo`U--eG zv`J^x=bcGuoZwo?+xyFzH*4FMGT4D%&ZlK!#}57^Qr4IHBA-j)Oz_|5f;}NR`*_s1 z--=ag@z+zfPIVC_846*QwrvusNcv@_T3O}xqSSDLiVyBZy1h= z*>_||<;Bl>KfpD|dF~bsiGX26&oX-0`CSS@V+*I#!n@h*V-7U`VP>Y=@G5tAUi#Bc zYjOvN&Ic8Nv0PWZwiYKQ?|a8NSZ_@qMtr+3VyzJM?chxO2@#h4{g1m6z9ZA`txobC z=f8b1OS`dGHF)~-ulu#3A9!F`U96oL^`+r`ZO#UJ^GjV4kejsZ;RcSIY$%7txio47 z>*2}qL0~sX#y~^%5#z&dVwyo(%235Z3skYLel0n~Z@+i(A-CbV;@1YRN=99IUeP(|hblAfuz6u%pn~{~7lx?X`3iSA-S_dy+uybK1Z;KW>53YaaD7@?TTi+kR5)MT zZ~YW57dSz^HvLHd{N2dQ#(m4~`n=|{Xj<=m5@6@|<1U>iq*&0TAMmtt{TucP+XoVp zQ1(E3UXG%C&99RR-`qfHN-R(UxTL<>Z!f0}%Mcb5ta7Z*X-gzW+z-oyiRjsm#B}B;dNJ{hzFz zI+D7F-6I52H(Cu{JvchC9U8iihAJnhV+l;N-Z8_9@S=m@e9kjd)qJ5_HN{hw0*L~y zWL)>@Vc*$Xwqhfh<&>iIM!SmM=7`2{B&wA;AQ)tb5g(h>tZ~FW=2YypDvE{MSa_ex zFk8>-sum5VPxlDi8*ZvVKI1GuV-LFJR1(d(kX!t;{Yzs3UyD88+BEWWj7Dh^ya*KM z&5xuQG*WM>hg}FN5<3p*>Sr4JMHtwvjo$acBqd5$+H;N4+{hP50NnrOZv9LjpG4+zM!CGp_|S8-*h`TB%lS@M_n zHLJR|JS`yC{Dm3&90_A(Z@`@#ub))te#91mE?kERWOG#mY!9(&U z0ozYu907g=u>`ZB|2c#A7nUFW^Yeo3e3!iItX+pyQ1P`Ly>jIixhy}qj4DUf!6y6} z&#_CX2eW~-#VZQ2`_u@qx8{aVNT+W};}Qa%{ib;%=hNx0+d`i3@8Lg2`~;tIBngv4 z@&q;z)RBw#!JT2Hm_qy0(%l+5r=R#fOO`ig z{A};p728Zf6$#MkQ^Eys^29;IM6vDN|Pf$fl;7$_>roZy_ zRWqN+73&*{SLr=Ra3;M(?Vq@G|8vR7zqW9c2NN(~w)2U0kV#lD!Zv=h-y`!04)ro5 zRjw4}yKvh>WK*(_`f&_yQZvXwIr>UuMcBT|C7adq-6l+NQTrR^zT^kXytw4Yr6QN5 z3j2tC*ga`-LX1-7(^oPW&SW-Px~=mqSSWW`JQ?K-QA&dN^cRQyFt2< z&a-^<`L6Hv{m%J!{&;OJl{MGgbB?&jJ?`=CFM;EKItb}xmIGFv;y~qOOplxqRV_-y za1!4+1|cb2jJ*Xxr=T@7=v|au?@x>QE{G&vGEstwg^{UBppnPJQi*hZQ=aA$k0dL3 zuz-fiek!#IoPGt`gwaxTFr8<*KJ>p|FW#p$O5rPJ8OO8CNd(kbpLfUkRdUuNlR2*T zG%>y#wJX+La&G~n{X=0L<@1cmAm9*~Tg`)LuGLG93mJ0o^HoXwQ4;gjR1}_K5Hwu; zEr0L`;SwT*K#=k}s~`+kOud0wwqZ`)M6*%{S2|&;ghdI2ql8Afj-TZRzkQ}gCMd8g ztt-g5r)w=iq$wH50@URzU&hMr*RILs>Icunc2)ZZtH5511>h@?A4pc7I036^)T(Cu z54oLROYmU@b(Lb4S^1-ML7^Ced>G*Ol2tn!8Iv@qwT9+YsDkOqo;#56CesClE)`+X zW&f}gps*pq$l~!PEna7W4!Gc7Q2$SJkrK6HwvI`4mV=w{u9mia=W~=ECcDF7XupVI z!$sfnx~9krs405p38BNoi`vSGSg2e1^t&Wf>3CDLt|aAg<%zz!vW{M6%5e&CCnW`Z z*U1T3w^HT(|1$o6?w648A`-y{8=CCH>%zbwB(j(elh<+Y;wgb)j{eFs-9=hCAza@F z1T4b^;$L~5MGK1iq0sX8pexkW;Czl+!4J;fuYBq27bGSsEloTj^IrNG@r;&&Lb6?m zj;_BruOaFa7;W_Hy!5=sf8Q%Qi7%=!o439bEEG`A1vV8S1tp<*WhUF#)8oBSEw2YQ zG9XqqY`3RSbBT9;C`!pHL;fg?RM0JQb8J6@asTV$o#^aPq!btZ;;dCV3TWsT`2euA z^u9kza|qR_<|z!-h}d#*mN2rl0r(IO(0uoN*twkUkH`c+uEqbS*aOkKB_D+e-BD67Kc6AHZ$t+WH1oQyC% zGbd!QX*jm2dhd(Zw%64Xy~-!m+0__r)!bA@5Nc$~^HsGG5ZHxa!*{Q{HMQb(;wiw& zZ(d^ig&7x<&@wS8w0HJPN);8+=)&Y#su8g}%&5)jVMEy;x8K?b{_oLjzf}>AVtm{I z0hwnGv9c;z%Ykl;cRAFE1e(IZbh1U?RCBn}5OEg~2>3=@B#{Wt+X&1{0&?u|NwJ$; zR)7e-@qI~>cy)W#arZF)>#|*zE29QXaLt_pd?b1>>>VgqHoeJ#Vz=s2H=^O42t%U+ zA=RXHj&4pMnpI&-tcH%xr>*~#paafPw#=8tC_QD=5fp)NbIR4?rM<`%azp%h$$1gO zE&U*-5d0CeN$v?Bgqv|pBaIL(z0ahjQF%ZFS>ZdS$J}ST$`-YMQ;}vH$r%O2DKMu+pz4E= z!OGeiY>P$}8S#;LWX8$Pm%u|+n_Xown6n5JxYy8@5h;M*S zA>4f)63E;T#@f%Gc76knkuy4wAEqQ*+Bm*wK%Q z(IQ*=61Dc~t$s7AG#%gnRN4M@t(UnUYrvv%U+;}dl#O6&UI(b^tH>efDL^j9hCOJ* zl(C?!HVjKU7eOcz>I!&9yRj?dyiit(cogxTYtM676#{{3d&rl91i-6^(^55g2Yh;G zTY&huSCd^t7K#`>jw#2~j9cEMRFk6F`Tx3v5B&%PO@aYCP(31Tg^zwHfs(WVG5W#* z(=CN9`N9!g!7zEUAMu5*ciB2!hwACwT_IGOegT1`1jgP-U6Bwh0%C7MNC0=dh%Axq z{3|MbVOiD>Q7c#ZqS#nOa>ZuaXENu9GeSK*{);Fd+Wrm}&`;o=fDDr)E6k%&Vyg5^ zpM3`mS{q47OjZz*W>w(EHi*cbZUN}*nZfGPUdJ4w1drF!U=XmsfxXTFvGI$rPM!h^ zX~pmKNxJ9^xkUB3m|OcHuuq!r&$oB1FmZ8Z88sbqi;D0^P(?FA-iPpy{?p@qki^7i z+vADV^*FW< z2}1d-<{w4WO-rtZa2yz=SCW<^X4oJ}^&v{nnUTY6HUV>oDl`(X6{d#=XUYmIElmj>0l_?SQf-X9eM440yJ!YWCU8Ri)H_qgCZgs>7^uyVR^@QV=&Ft!DP_V?j$ zY5f$wI;+Y+SlVKu%wX#=lsr?Y3Rb~*T~mbM3<=2C$+0wG$8%H&QJEuSob6JQLs`iK zvcCjLRUuhm#=2?1e2g2|Y&N%kV&&`Ul12u&ab_*rrnN4BG$uw;~p@)p#E zjuZ#QL5M^d9D|%q)?k3yqPzBHvhnTAeM9NIrt@M92A9;q=Tctasu0NBfB=DLNGSQTNhC^a6-b4t7|5O^x8)t+5+MN$WtY0J zC`Anh9OE$&5jzcmV$rBp!Wmq!L4KrF##?clxdWFUif17%T4@4ZT@T!6|KhJ5@nl-s^ zjCS|(&Aiip|8|d;%hIr6c=%$TAWZIT=^_M_1aTyO!cOsZo7bo+9IAaR!svkx5$}K^ zpBc3qQXty8+CG&=^ju&{Y3~y$&S?ZZmNUZwofK zECZjE$UDF%3WEeo?3YmprQziL7(qHi0$2Zp0pq_O{!z%tABCR~8s2%N3XAG_GD8XV z);-T14ui1kqW0iI!x0p``kw?~!smEZpcJ(#*C{NqmcMah?)F2Wj2FhE@sleI*C@hH z*Nby^R0)|Fx%|u_W+*JuBOj`|K*PYGm6qZ9igo*_gQUau)fc+IrFO{05iWt&_7V@% z&HyXSl3i4aAJAw}UgmcSaNPdBFfszci()F^hdp6LTR7pDqU-)Bec~vt(o{V?*mnxw zD8ZUFJF+7q)z|r%nTh4=57Ub;B&DQio{kW{u`BHc|MfU?B)|&{cf$i`dCx@eZMAR& z?C1@w3W=ydu@D`MUH#^~>xjCx8<-S_f&JYdr6;1xQ#gPV5}*!c!~<*Aa>r9pMRKaJe4OHP3c{sAtf7=$X|#JxoMnlwfP>S{^Py(;AK&($lU4j``; zhT*QREU87J0QQJIUO1i!8}%t9VCo_;!O~69(6FX&?hWh+ABX4f^xQ`_OVSTTI}QEB zKTc8Klaa{3Wc9hC6dlNLItV>X6N_m)@@xviFoG&E5rhgLdvmH7%37}aTyl(v{y`}7 zYX5!wpU8c&2SJ${OAygTe*_%Zf)d}wqi~jI5a`448+DJcLa(&wkLd;riQH#fO*ro2 z!ui=VhlgL^oQnMN1W!PeM|c#*e`Kltmw`J96B6=AQ7M-%X_kx^c8Hae3G<1{)_h0v zEI9-(U?UVGD+px{0fjNAb1xGhFn$h2Z2iC1mId(DVod_5ZQv*cR&I+kJxx;|6C)2?f)37uJukj3R(w$lh6JeU3D2t1dD7i7vl*aZ z?CWJLoz4R11K2Z;SMmLok_JpSM}iG6|2k#A-X&`kpkcU_Yi0|dHQN+vQiw24yQ-Q_ z$mE@3w9W5X2@I)YH3uwfK#|2&YZ5rEn3 z`5_w_oZu3kqB%`}lybPT5{s>E3gGO!3egUh_$dX&YT&*gH(b8{ zTfzqr!wZM=m4FRP)tk3vE1Ic_%oj0Y5KN911_TGh;QmUJU?aU#@h&%u`pC zLXTD;IwY8W_4geqQhgGS2mWzVq&R^gAwNtP-yp@V_ee4?u+c4;ouZ2#1pUgaaK~Sp zsZV{!Z>h7skYaegT@4t0R|Hom=8L$gl;!14N~jFu2qo_+4O*^d2w#Mbf=DVFM6_1t zOA_(lCJquVqyi+ukFtZ|ujOjZg8~1Yl0fkyE_!1XJsM*N60j1SjX-G3(z*H}JCxL1 z7@h(Q*%$D+`&%#cLl#K>4EWc{*@*b6AizqVZ4dJSGH{U48U!-{*kKtN04uXVTxrGL z0q}<@j&z(}UeF6AHz_Jbsd2~7Jyz2LRCSuazpHWaClFW_z6V{81Q$HC{8{kEer!0I97frsU_5qm?#gHFx!a> zmE$hH%!&YnY!NIrK*0e!IvbHpu@-9r3mRT6?}riFSV%ac2fmMMR=08|%- z=Mk#p|Nf2iB=J#>Ynt&i5_MhK(49 zx11=1j@d9*fn5|D*&<(q%=o>tG$^d{C4a|J*l-bVL6o=|6{H;y-5ntak^OreK#oF= z0hD0~Kp(kkfuSEjiiME|+%I#gUJHrwY8S&Qho4^qA3ER>{&R-WKiFqHwas1l6yb#6 zd&!DfSHmQJ)^W^o^B+%{g)b79j{%fhaRT$f)$kHMI-5b+=BEHf7aAnsq+v(~2TV-C zR)bl}&RW|IV3QSqP0t;jqT~2GTz!mAP9X!UQl*RjmZLc+@KF|46+{DR@reTMl+is% zP=pk&tMOxi_vn%U_{Q`9fN$`g05`_9P0fR<1|S<31!Pg^i=eLC0LPg{+dw(rOaKey%`xvY8g5j|o2Y4l&EX!#x&CBXFzv zXn1aBaJp@JPv8d$m}bUU1<`%-(-ampR7Rm1w-j>R8PA>OLBu8{&0Zq@2O!Ez`tv zuyl&@m!-zxE{m6HIR@rtpkokZ16||mI+jXO5G`0y=6GBTQGR19b6f(h()FT@va0H8 zowdx4joaL3vpx9Oo|yaTJKomn)t;1^7Q)X7I{-VEP#|%qk7^qwL@LxTs?%~eTjsRi zdFU#AXy`MKQdhTlWZ(MpDf?FsySCmAC{|Ax9MWf~I`(CWhiLP?Vzn~y+S_OSG1qYP zF86{{X?G3JL>u0~Y~0oV2iAHb`^6bZnfL~_X?AWNbKI19fx4KGpdq41y21f3DUQrA zmd7!vK0&x2;^J`FM?1ll7>?N@nSNHn0Stz5@$rfh5}^15>`?1X1N}8xu;SIT$hQg# zZRo5Hu*dD=o|(%7PMf2an{wDs?^>NLg-9bjb&bv<br%LLVHR7O;%Tc5r< zux;9Wc>{>i+HuZ2xrx9Y!9lY`zHEB^<_ycl!_c14PUop7C{6Oyt~`d(dV*qX9?h|L zIzH9ecK25>kHxBSV(5MQRI@yUL*N!bwqM7c6>hrVO?JCL4feeNN4+r@C7bBpdN7-uS)bh*%zN+oBB1?S_b1`BD zx{FZ%p7nye4byNjYsu^;TMR2ouz5<9LMa-#)4wRauiqm-l#6vWoV(8Vmxv%}-UxZ#^A6Fmixkr}-rbI@ATB6NF4=VSM3+s&rb zwdvVS2IF|2&ad*Sx)djwmmZj(k56Pz+sca|-F3>?4yU|pk)S8PqRSP~wH=LxZ-)P4 za0SqPqJU_%?zZGXNecVvusm~nN?aNQHAbn2Dm61xCUCU=+UDw+)0h>bZ2sfcXS=cE zkrqOzm@z#T=kLUa9I*9p@6TWCf1NJ2W?erKE0%RgbGh5OV5Q|Z~vZ^cGdp6ejBm$R?iQ3wh3EKxxl#64E5A^&T>749`49`cz7> zv;&`AkcjN3IOO%7r6?*3W8)-X4|kL~Tzjo0));Tn)xS%Y%H%Lni0wAc;R)hq)M6Vd!b33mK;VF5RZG z0x8LN0n^#{H!F-ahJzmUH$N<&dm|B0GFyp5zpE)?Y zl$N6An`Sh;kyvE3(I(4?>|0iA>o?kOCol}E9#e`v;$tb{PM=`^pr0Gh_RK4C>`{TI zjn4}b=hLF?rB)^x;%Q-gZu4T48CRp4O>qoBUW`;Z(=D|iQ6^<3dUPyNF*hLUJhQK7K=PLThHWX!Z>d1R$#D9=?NMq z7Rcl_+KgWpqiNK6iP|jGC}{6Do>(f!MY7vpTA8C$cs44?%*C=R2F$Y0$5T;lfLi zQw&+w93BNjceAi7Y`ok!-XEukxEOncp%|%G&;Q;qsP)lVBP`umDKV`b~+thj4}c{ui0RbdS+eYI>wLpE!@`UK&JAYzTAr;2s!l>hu2DYXjHNw?$WOpuF%8l<7Ix0x`6(E}npLB>6X(QKpW)`#ROpM1qVy z(GI^WpZ?CPie#SvdLVcdbaCQTQcz`>ygdFxM2QXvdTBH65Q)-5JPr({^h3G3Hbx7N zQ(k-AP~dK8Bivit8h(;Z9372a>0$95gW&n6bKd!8+_Dsl5ZE$J=-iN+q_w{@2*2E)bO7-VfyhZXt+TUjRq1^Xs(_0pc00D!VyuL@COVCeY*mS0XG$SGH zM$>InA9ZY6=NAQB58Zksg0<&t)c2Fx7Q|H57vj-db4_Mp+u7IO6EA{3Wq(gOA0GI* z@grX04z4FJCKrLLiBf<=i?ivpFv=D<$4%HR6xjqL)2c-)81Zq5sk`xH5iyEn*)6{Dq}D}c>jW`m-hbU4eQ0| zPhFYZSpVLqW=nM@V`i-a;}T8kN00H7*{rH5?+` z)uu;vK!%Yy+wzyRyk<}M8800xGG27mmqy=N72c;~XZ!YK1|wT%+ttREEZ7pb%!WHm z6iB_Sq?xmr%>W@xELC zb1_GcW98@CQCu!9eoRe6EEK}2!tSP%IzPLp1q~nCuJYO_>qVmBsTe(gX9i|hblieQxDJ61|XjP;J(?KtKFbUcU78`Uy_v+Cd`!MlwNwn zi-sFK#SrIt=I}haAcq}+Kqpgw`nRAlIRfG|5j@SlKHBg+P~sH| z)Sp`V-(E)++Vi9_;|>VmGJF55=~`VPZWVvvNaladB?7hj6cwHB?F$gR)?W8MRg)=v zF&Jl#IC)Zqmu?4-%KH=dm_V>WM#mvFh_1RWdUIZO)3Hss#j^Yn_8WCms%u<6}R25!!i z#QFm3FAAepA`KKZKp!^+x;AdFhfdGOLF}qoZkxL(+J%;?onevj)H)}PTh{W~lrPeK@f3HsQW42M-dUQ9}$hd?74tWxcNeZflsMj4aE zrC>19i=WbT3&8?)#XZvxA`a7#BN!=-7dJ3nH(sX+Lyv^nP#eDf))JfSaC&5wxb$hN zJ5?Nj<7FF{pWEA#swfXnyLJ`j4ZM!u&6rH3*1pcVoS%WlGZ=cDZ}y$eG)0o2pP4)I z$TYZ=xCqiS#}+WOX&3BCTH+*{QKl)Fkq$g{xy z&vGA1&rerBo-RFnzW5mBj}q}^i`R>F9j}qYvi3U_GQsl*oUtAye%8^m@iax!he75D zq>m!a0J~%__~@v41C}MEJvS0Pf{j~{l;SqVH7nLe+B!3-qRWwy##585B#5w>5Kd?9 zW?c9-yDt+GAQ&M5Y=+53A*+>4{_b~GlsDXjA9KMqy*e!R)U*OnkMy$^&s@gyCEa~U z@UbLeEH7xS(Bj`vTCVz^WpA&TT$`3`i;tLFB z8WI+Q@#^(MSb40_879##G~u5VHylWDcfPTyG+6?x*7-5U*^=Wf&!EMg=rw&?-UdLo z{BP1z9C0e!@7=i>-!{Ya0N(bRe`Vp=##18V>+#Z9aptAd3YSc>fdiLA(N?rVds7@W zK7V5z3LOF#xr>~4XfMuJtLg$AiN*%9t%K^z=fncUM(66ier%t*we$BmjhwEB2pLS7 z`$l!BU}oIF_6Ao5Bl-bxwRxShBMxg0JWr!+y_Jx)?#|#>mRDH&#Rkahb4Mcd5o0K? z=31StEqHRJe78(Ir{gxG{lhxOoQHe);WHR=C(j$(be}e5d&eBvElUzjZb$^H0&)L6 z7j--iI8B65rlkTgyjAn+=7Um{m1hS!mvwsyiwt4WWqpEy{!zwdRY_X6Cmv!3RJi|K!=hE*hZ0R@+jPrV&O=nl9iHh1X9C7VF+_ycp$-hF-j4B!+UsN35f|zQ_noRe_01S;>q& z6FA(*B&L9tJssx&txb8`ZJ%o_>f(Kh>R7~*^6ozkI%8&bo-9pmC?PyPEiZN`zb?NL zY)1AQ`k)QWa>d#ccz+2}u(4NAV_(?MEW8Z~va9 zQNkJOAsg0Qz#`M2ia|g5{wu5N7e3wscy0TH)%~@SwEek@njCvs1&<^Ej}Q4h2m}HC zNe8(hmy2DIE7jyUCM9MbZnEbyVOZ5!w+&Xb-r4rD59%3zn|$}-0Zlqs_c3t|82-gQ0v&@w8)~8mLc}$f)97m-hu)UFvN%=C8-)jTQiI!D z3|XlbbBih@0=o%pfpcV4T3Ut!P0<4dJ)fPj31BzZ9*jn7(9#kk9D3j2f<^h7zZqZ+ zPEA{ul#*3P5=PS-rrGTZ9=xbr5|FbTj-jC3`!?8=l2_I6 z#n61W9=;Jjh#q#!)BL%JqHEvA7*X4%(*CImP|(>^_-)?T_A9|#>vGt!8jX#ZVB^I_ z-wGZm2Kat9TCOW|(M&1<6*|KeN8}e)!WGK$dseEDBUXdg)+2V3ZY?{ND0>Az8o&Z#M048M5)IF&DY{;PpjS=`Te!HkT>w&6Nd|eg zSygZ5o#M4{ICF&;FX4qT;R&^2ZwEF#(tXqaLLSJ+gJ}WFfXb+K_JW`DhoiZ&+!gi7 z84mMs(?Y3bA~`YvtAKlu6RD#|(2uPMZcb+9p&_KEvvi`=9e=A%ugp%R1J zi{uyG>->$aXX^?ncg3w2{(JeyyS-YgpHiJOWgr01}yU7yHE{ z#cWlXtH>SeR&uRf-1NXk-}6wb&>F%h`r2}P9A=)LFEUG!T8b@b(q91j$r%~`ThIRi zPtXV896())#ocs<>N4zSl7((KSQ%W-9_wYDT^G9F|g7l zelhQT$FPEx5?U>O|J3xE!n9SeZHKASm74Wb?ioNri7G0K0wvbn9z?!RIStUOp&`SH zP3-MoV>5_uod%fHpIIA!A*|lsL}!||jMp$o@0C*Ba5O^~!*uKn5EGUt!Wyd7_}v^K zQq`&OBJ1Nfvi2gcEFadfX`LaWU81PP_IUZ7v6J_=1|;k2>#n|7|I($hL;=VNq$0UO znIo0~qs|>Y)TD}cm1tui4`4NgGdm27+d*X@Cr7VH<4fG6c5gS@?KZvKC8QB@cH; z-Orz!etp>E=eoTc-rH`pT}#dlrcZR4i&!@vR`d4W**BKG2@J##5%p_4dlvm|k@O#g z_ui3b;~ykgn98zOW&5qk)61JA<@~5wUi-q2Cb88>uXFfWHZWvfdc#14i>Sgesz#!D(G> zB^s2&G0+)uRZBRrf(L?zphqi;T{g^K8PQ-+)pFMQk(d#&TZwRU6DX_OjGhO)W4~db z>&J_i^3AV))Pc4-&o#STfo3(yPs6tmDAlAb0v&3Y$-a3iMLj%1+&6Dqp37i(0d0S;ZFYT|uV=kmkdrpqxy|Ur$i$Pubjac}xu+$v zo$P+bq#79-n7C+pg1Zd<3?%yHH$KxMhNous)C;aF*euk#Xf0#_Gn+$3I{RCbE|jb+ zWmj>}H!7Pr2XH`xM51HPLyqGjNV1G8*jSB&O|0Qe`>`~ObhPDJ0&5)Hp7IwL(9eZa zsxsSsDw3pR$&k$37?B|_7x}z!^|!g#(WiF^mbtb>wjEUf2C%NCoJ~ib*mSYF5OH)|OqaQu9~|S9 z<#^TaIyf5uv`nVd`Bq;arjMAYzTQivlX$=MWECOWDV%pWYs`9d2C&GGXYE7V%q_;{ zcXfBKq6_RUyB|wd%q0Pxdj)NlLnFB=z2AvMd-8i^BaLLx!6!)Hw$p$zq!7O_+xld=THI!fRqy=PTO1I9^Z{pUAFpWxH+ItJK-JDTW z_;y%rE%{{NPiPG&YpR0!hpZNK#er0|(9IU3o0jU%eVj64emI5Ke)1-eBUd32|6*v+ z9oh8my9ZBn;k@$;V)_C(Z(FaW*XY zufBH2i*IA!DzsZkvPbtPd7d}{=x=s@WbN1{4Fgl*#KXqOS6{F~ah{iNBL$XU7#-IL z3;aK2R-sy~B0JE>3w44!_vrTY-ZX-S*`hbF%f5Jp^I9H7DLx=@R7PXm z`EW5P0Pe#4<(RGUvKS0qz^1AjZ~FNX4kUou=w{1zyfK)49Js`3znsiCSp1pV{(%%( z9;sRD8B?gYcckk3+u3yvAkModtX9}~j%;ZlaHxCdMSW+vrrTj;{YuA9(9wG28)bnL zM0Bo~Wb3}PP|2{t(-8rev!)2$1R`n&fi^vqY&VgwTCo68TEpD){hv(r(`v^>Rscq; zi0yfA=zg~{K66jp&yf9%Yyg+JGmJbu0xQ>yv+>W1z5YN2LBmkK-t1R9a78zG_&Lsh_qkzMEwvdqy<4zD#$WOfINibX$|re5n7qBc8FxdHttRKu84>fe8e~!XB5TdlDDz zlv9}StG1bMe00!w0kdH?#LplhMIgX^>|f7b*&(hyLMa@jNzv9JXB8)^rNiF;70N+2 zl+(%A&($P$i!#|;kp5AYOjbL^+1Vv4^Ye*_X7jFdV)JC_#5UW?YHI#{t+eiK z<3KSYoi%IL#XG`bi>m1~sc%egn~Nl6%O6_Dg9)e96kYi&zx##Z^%ab)7t;5Q#Xf)A zeC6d)ja5zFVR>{)LV@`uwU_G6Qfoc?*#Y`t6~WRjSN_U<>_4~^2Z?A@{ux&H!g^?3u`CVF)bhN$vn08~uH!sRw)!D0f1`2wgY`>yy%*I$z zR8|&_JxuwreUMG|hqo|}@Z=s7%!ctsn8kEmjq1%C+BLdF7j}2U*C>mVX`l2F@FwYK znfSro??&tTmo;CD4g&OQW!P|k=E*%lj&H75K)WBxPY-cMTPOIRoDT{k)z}0WA{fyM z$~rnsqtcR+Aw?}^HKJOV6OBPEn+qZ)wTH|J%L8m?I*#r=!O9?JsUT z2^Qz)lYj<<9h4m}^cuc4He&MipO5$8BVTQWE& z4*?_sh~F6J#=rnx3dfS{-o^fR9!Yksv$va!GXHTNz?byH{AJk2Ri9Ys z>{1MvB8T*gn1BENQDpV3cz?CX>}gl&M#a?mXun|kO$6U1>|@+)jz5K)VR}{|nM+X} zMVHra`t!KmFWyqrbHjF|!6iuP<;u}mjAo9jfw~MQRVLB`YnO~L&=S{tQkoD`8sf4n zKmnCwiALe`XUj&ymOKv(?|bGKrs+k5uy@evxhwXfAZy9$(l??$k#rUA9irTY$M%o_*mxo!)x%WJ-V=m`t7kj8qq2F{3Fr1~%%iLIcGAiwqvs z*o4VsVI6HXHW3L^Ny5>^i&x0~Tg%6oZhLo6Plv4V;R5@weC~b|e#m`+&ctCF^AImh zQygW(YLQYeOb+X9wGt73Zr=~QL>noZ{r=zrZVE_C?p~1QcBECQPWv}8>(7(P4gGE{A3geWQGUbfz@OwU2tq*e z32@9@D%8rb|K`X+G61^4uZ2VIIer_7^aLdL2zVvn zzk|Gq9{tq?0bFg;e_twN7l;18D27q5{_IFpJL;qVdDnkGz@h!Ko#cjaLuVoJfAfQd zU|7Fi3D^Cn2%`Vkna}V>CuB-K}ImC4T{U$#ULjLT9 ziVX|(q&38GmwnuX$Ij(dJT{(c$3Rj}31SPcF|EJ9zk-G zANZ+{76u~XW?yTbw%_IRp8}XhWWC7$5Vb#~K>sZ1aT{rFjy`R5UKQ{?Y-HJcV zKt@<_KD3lEagwm%JwHxU+~M5Tt-l+74EIDPi^o4q5AWp|C1-w+_H8?(tHaL$?GKCE zVgGg=)+YSC#h4#X;^g77^ck}Xf^HM2J7S9FE(?$L+g7iK>V1jtpotw zdtD5_u!%If*9xdeT>M)}nV6jJ2S@$5(*&$n z-?qwvQ=hg`OFw#YfZ)NN`?S^ym$E7RH&b-DyN{v&}V~8j~==nLU!f1H9>jDZFxR9-$vT(jrrD`KSTctET zE7f)wz1=^VPP~xUvuz9(5VPQOQ$m^u4;2y?WzS4ZYP$xwmqqsmJ~+QG_xS(Zjo~w5 z3=9dzxiq?4OdxMlc%R^Mc=)-dr_Xte6Lw>L1MqCJy20ErXLoqHEw@NJHe$R%M=2yED5o>#2~SeI?2`3u z43PM*pNG9;@|;Z*k-O%6CW%Pl&%cw43l( ze+|&SVmmGF_lFI&w8A@zhv$bN<%|p+V$#w~avV2NJ9HHHYYj07h+EWg$-#>kVadvc zN#<9S@5rxRNB{CNz2BMX3d7xzw)`W}Mhf`hnVcu4Sej}ha*LrUilqfCamt)Aon1k) zxN#%*Vfrc6TjY0*{7dV%EgOLW?75+CRG5`iU<&CysDy^U_}tafwbrk?9M5vjY=B_z z(S}FSSI<5)Q^@9-dd6LBvKn`th|$?!+xj}v&%aT4)_B@~LU}qJgw4~c^n~34 zooaFDJAJN^pvkDzH{!9z3Z7hn1y>mZ&)Gab@sdn*kWONp>a?nKa@#U_`PEdTw_;+2 z_SSVrh)r>=kyOHHd+N#?rPv{8**|Vo=*z<&`@izA8?w5(tMqw;q#M>3xnGEt99X0> zr}Ku&@P8OM4vW`%2@(iN97phyQdU&Gmrh-HOk_ow?Y1wdUa?_%3hI3JX`4Ap7PzpW z+*HyETV?Is#$uqDX2+#8_P8gD6c|U zc2-fE+fw7#wKEurw|jo*{X6H0t~tNHfLhN}@wYpRq8MEncSq=+Kn-KAkx7t0I)h-W zQ<=M`VohdfFGsL`E)>)j!Qv>+O6Vv7wX;+ACqF({6Eiz66nlU1p(k>vpuUi%_&QX( z&w9#u><4!QgyQ0?4?mtKiQOSruAnyeyP4yAwap6iuV;6J!?C%kp_cUr%y|vdHs&MZ z#V-1m?YFT~&I7XsZZ{o7sUtz5;pQqC(dJhPMnwHe|tW%Gz8;@Y-`*E<Y?igm$SCeJ&y`-}8!yTlKj3(#g1j&4gPc<`>m+w}Ghw)*8g=f1Dj~6jk*{TJN z_XVz^nTvz@O%pfxQ3g{KO_>7Y^jcx1v?v6R9KC#Cj6Y3qnj%NhW|>;a6BA) zlaez6Rux*}+WAi~-kwQ;|GTG-TpaKx|By6?@T|Lf8ELD%uvm6FP8pS>GAX^#>Vi<{ z6Z(}IS877JbMb;tQLTo&uJA2+f2giow zXu(Dck$N?fjkR6CVvOn?h`AQ`eDE&Wefz4iVB~A3R;%2fLzi2XI`nNUf?wUutTKpu`VV$Oi?Q_N6uSAO89h75wX)AM~V(e9o<0TZf{QT=grQ?YI6WKNV zyIZS3Y|Qhs25(c=`M{r#K4r0fR|3t+{YdEA>SaQXT&2(tluCKO+KUiLGWz!fQeP0^&=1U)2@#yH7 z>FQ6-#oOK{1%^-4650V(%Dr1GHrEmSz})jMDr3b^*8G@&TE0DzLajGl-&;gHdNI&r z@fN6aB+D#+aMzCk95f8;xAZhoZ251Q)D%CIg_8|mfjk!L`+~@J(%HQc z2<$EekRNTz-X}(E#?8tS;9)V{g3AV{SQ7XbBbsWINAbAeSDO0j=e|GuEc-tGT7l)( zbE6AdBP1EiUGJ{#W*-w1pfck?;vKef`p)&}1N+X`k20rB-iLP|b*>IHtT~F6b#?v1 znsw%OZ3LD$8wkSNq$S8~9rv#6fo3os!|C4~vqy06|Gf*=oPn<;Ef&Iaa;96Ve9!mq zQ-Kc2(-Ei&Y}v-%>-A=@)-Pmb?=7>nplJzTO9?u@sx>l?@>;W824g1MyVASzU z?0&K5PxX$?z;-*+&eC)0;tD2v-9Gh)bK0iLo*)zFktWM?QsVb~++IHV_omwPg|r^W zajT_W`;&N1yuuL;v(N7b$P5}%yB0QC6#A==>W=w4RDML)uW|3{0%Ra13Ab>^Q(x}Q zxTe!{{*XuU(b|%CuXs5ZfQOIdTc{DTb&u@X|D^5{=k#Xj;`-OoVEwz(a%#qR&jx84 zvrn>8T^Z6pfd?ljAu6zUq;g9~AqQ`&Z6>som~O{QGqPj{<7g5pA9Q5DO)}YWQGALz z?wlGk`}qO&h3C$ML|&q`n_9~dnuv3YbM9i`7mUSKVI z*<6zG3r}c^<$?g1FJ`grj8d*}>Smw2F0_DI4UVu`H60RANBW|p9UyL?WAAj+=$+fy ztL99QL3(%8n$ZijY}V+oAMV=UR}$QSMWvPT@6Hk^VqC`>G<%KE-m_bN(mSBdkqn-6 zB4wc9h`u$ptb?Wme{Q^G3E;fJc<)J1Z%F)|AshK%VYRB+EIhu3!iw9~$e~yJJ3Ksa z4?22@cK>Z35du1!ekG;>WEB;#B499cQy_kES&eYq+x4xdjs5`Uo`IHoy#TTy{+G@1 zeQB|c#r(qof$fFwTblY|794;uP_~EzUc0c0m8@%HRlmqsjw#Yw9G`RgrnQzjCE-hV|1|Zmctjt-wi~*Nq^F)+y95Q zcM7j`3%W+T)7`P{bkMPF+qP|+-LY-kM#o9Vwr$%^{w7^qsD9fk%-BZ=O!i=B+}bkeIr800x(B#1G15N3`t5tk{f(!JIJY{6+8hmba@0D zNAom|{0rvZDqOQP1Wb+#U8v7CVlr=~JRjGHI8HoO0b^qfAOShJ$wo{z*2bPit{>_K zs8HgG4U`SwymL6i)7TH=B}7z6$`)^PR;B(IQ={7JLx}o+m>K{fEHxv*ZU2R}neN-P z%x8jTtJ)WUbv!Fl!gm4pNDDdTWtv-q&=9vd1i|!Wbm@N0${IPCa0S#VR^X2(K@#}X*$E)nApWD zKLTosEWeV~tUdo0$*@BbBu!1N3!t*4)reiU*VZgk0d(O_Ko`COX1s*6Mm7<+&(C@_`?zqMwnFBvl{j_fe= zG_@y%ItC~-Yst&<6s%TF)RLuxqG^rpbN@y6Kl!^_{)(o5>E_k8piqG>gQf+h)n_cv z747L-RMV!VVfoOP&{bqL#5=xFFr6nRC6-$wC6c-A8Z1K0M7c8H38kulXoT>n2WjP) z&!|KJ7-Jbp`rh_C*1$Ty=gBSyWK-VWM~@@p+xjtQ0m?cav1HGe5Z#o{Xx`-8gVrW6 zVkOu{JHXr(&#pbD1L=30JF9$c6f-;qu5Es~6n}RSgj_QAsuu4%E*`7H|&uPD9 z1(#N5@GB{>uAzI1l~X6AM71D&J3Mi%ePvMT-=CsM-(9Q7)i~#Ks;4o;^%)AY1rxIb z5OcE`)G#Gzm0Bm0qTyo$oY`>odalxlzp0>Y0hoZ3WJ4Swfsrmxtp2IVlK@Hw$(FCj zOza@G-m2uS()yfF!C7XQTWlb{R0>zGH3Nz--IEVYy7`$zAW!wE#{fJ$yr?{ZO2u@%ftH)y6zoIX`Wogy_gBYStUu9Uvn< z*Z%gHvYLJsPn41a^NtXq|MsxL>>-x<5l)rw>mW9k&yk*UAWTQti0ODOxl0Q4*P$s` zO**3Z%kNqc zG(e}MV*}i>nqUIcA}0z)lI-{VL2P#Ssv)9~>}D!lynP z{eoR_%7hAVJQ(md|E; zwXF0Ti@o-DM^A;d!|S~VbSO8Hxp%KAI<8u3L?NtPBZvHOrFd`?;{}ea^0XIysOg$R z9g7vLevq7;siZw&BWep%eq?FRQ-Hy0J_wJ0G6Db;`K!+i5cr!w!an8|MD?3Uoq!-= zmAxP%CLTp0!8R9Ek;!`2b3Zwqp@e$M#?LVUP!sHVe$-zO@OF~wR|=mGEcVpij1z9g zMt67!$)@cyR{=BLW4^r#2hhjyc=%5taa2r2+>YX_2@rrnfVEht+F}4SfL5*+Dnh9(YqxjF>$#m=cS2{p5vouw8NMuwfeqJ(E5*ePqUkX7 z(Y4*DPt15>EUDJf44XkBID3kS{p0 zG*?8bGKJ(@mTm2K${oo}0SC`JE~cdrw->5w1@y+sSu4J!2(`b)kU7(rq6j=oc($K9 zkVsNksQo<}WFN{7*gt4(@B1(Se_uKU@rs3Bhg+{0qkzBH<53LRLV}9^WaCnc?m+*C zt-5lhn{BOa`coIa4+Amf3yp~HC80VUUhhD^sjTURb2JZUt;Ek7Hha21*qUfcPKPJO@b5&samxTZLY2507f?) z8^g%;E87LlV;?s7mlAU@|7tz{O;Ir6K%i>OXsX}enSiTDpj~7<<-XJ6NNZsA(a^Ku z3jwzw)F&XK&wZ*$E{DK7MV6cLgLFFD8~cZJz?iMHZK2;Kb)@`U=Fl4izarF?6xop< zkHa|`&%D^7VMnNc&k8K$V2!4@5IZ%anhJ(?Iy;sjNPznsEqb$kYK`+e6bMh(^2|_@t?OAB zP9y5P#@*~Zq~xx^5n2BMkG^v=yaO6Y8op(V>7sC^hkTE167ur<#^%ZbVizXpj3=1( zkJh?+Tt}Z?Qsby50W+t~nC(g>Qzo4~asoKW*r)DZ4MTySPKhAUQYSljKPZ7lVF>9C zKbxYT3|L!a_Fb(ldU<1a=xCT*>Oj9#9XUNdR~Y=9_?F6hInF?bxw{7%SHS5ejGAoQ zxG;Gs+w`)M^^=^rX{Tw*e{zSTd`3>D6pKZ6=MIZ&3dM`k&eVHw338t!5%?ONoN;fm z8c^R~9^AWD(-g3XW6t|h?yE$g6SCgP$@o#sEtC%vuTHaT z7Vl7+H~(S-WK>7eoW8)}c@U7a{RliREd07?ug!rqbaRSx`Ts%&FmUxv@?en^Il=uL z)qeN7ar3-L(1K%uUSD8%BS*sa=BwQIy5+`yC`nx_K*#9u&IQQz1wCA34PT@W%R&yz zK5(`y5~f>dssA3XALm>PLH;3il2tO%@$$@EH|Z3wWpyQ!y!QG8Q)*?Y8*IXR3g}+V zk&Oz*Nbi9axQ01L49LM4TTDnY7E~*|-Dz89R^%=nv~{shE5PAf~&gcr2K!-0ty8_&o}SYF=@?l_%mmpR0Uw?ZS|!Ik<4VT^p*bup?v=y z^!tioEmsH+l*dP02sy2$D{K+W{%IK#Wv1U*v*!N3`KMDsU@l5 zod%Ncq=mpkJtBLa? zli)X6o7rMtW&DnMzAGVtO1wk8BW5^mQ2)h=c|?Ajm&fHmcjEW4`4?)2O@2Q=$#a~y z=>J8iy`z7x%KLJ%C;PtwL|^HDCl`GB3Xfxk|E}uuw|C%cCoe?c50ek5$FKuPQcy68R64WWAdt%jHC`;*V7kzo$&81WRY< z&j59|OXJ$*<*nE>hxweg{m=ik@K^2M6aoK?Q&2cKnd#z_BJ8mE6uZ!H?A09^E&D%p zL;;|FFKB0X24en8>}t#!(+f7TV2i?hOP-d3{5jEJMlovU_cG}|*tgxcf0uxD^ z+UlQDoR6imnqG>yIoa9E2W^HK%%XjCw==59^E7{$9PuMRfN;Rg@1IdlsnSnOIau)G z#`I{S5BcejC1<|3S~5;SU}>K}!*~4-4F9_PcZl~pf`#1NJ402Z8#ka@>fC*t(=@{(L4-ihm!Q;Y`_6 zfy4|08Tpi&Z@0>RaFBh!Ac+R@zkcou{oBv)4_skjA~OF{mIY|xNv02H1LSULXxlTS z>pwD>FHaJf9xt_~1K4ZN^lbisX!gG(&3=r3{e8+wn`ZIxa{!D17;p##&6?PM97#c- zMi2dee*_Q@v3@(cjNhECtSvZ$up|;4G78H0E61Ds#&P1GvP>Dj4|MT4Yj~MlLUR2+ zxuO2nn8wna;{Wyk!^86cb5ciir^~&(z$xC@zq8A~TmAvm`JIT7u1?~4h>#JBAh9 z;_N4?x-Y%cl{REAIeJ2OvbRC_p2=8$xFomnzX!Ga<|mdU3O*9bj-00_G=L@|>*7Kp zAkZW0z5kzk!a)>->6usG*#Z;u5S2FwcVCkBUs$cc{D1<)rm zqoeV-#(X#a_~7+B>FB!L?U?4j-ujv1Ju08cjqHRpaluW?ZvPnfbpa@x^{mzpD-r9Dn4R%x~}Mx7EGM`VW(Eyql!;2-^+v zzmbwh>^=RuJ?L)yVG`GOleo@t+@k)Ge&1<3-nW-Moqw3*|F4Y(6$PrQs=8hdp(CNC zjjpDx*^dIjflhuwWwRuAIGWHXy?Zb+noM7D^ENzTu*mv{Jy+;mLd%X9U6}0+(9J5= z{`pG^4h>sm&#@^|m7qQ!Fik4wtlW@j-viSACWJlmdM(U-mkH2r0YJcwiCYCwEFUwd zGTD4Ocz!|rqsn#uw$KcHPFzg!TyW=W&kG+d%||>52OF7fZz2_(3F`g*dNt&Fl?^ZW z3v5xa{l||yO|j$Au%hsj!e@SWX-UJU$EW5a{sgvTPtVv3jW-#M&312Yqh90}c#Ig0 zE)_S01HrVPS22OwevE8rwjpDxd*~Qi4mhO7#D!nA?+XgMvzUXL&J~T!Jl!M!6bhy@ ztSry7)5SmRHxIhIdr(^ta*f?LFb}6P)+qPBYVF6up@sHq^9x3m>LbM3P(om^2`Q^B zbxlu)9{DkvFD#%5iv$bE>Q1!vqYTu3kUuXB1;aAzTI^KK3n}ad6<=dhHJh964W$fe zupQIl=sEV-s+%PN`@%YN@Z&kGl%Z}cG*9gTHL3vQfEjoF)`74?BE<%3vD^P z94Vnp6`GBWt^9sjMQdwkD4|KwU?&*##|Z+EbdQjOMaj|WM2NV1)pOA71bvxhgvHjU z23t6rg?sHuzA^`JxlAEClwpit2xxa12AO@Sk)8BPheyaW9uzmfo`nd4r49C*CS}XK(3-q2*UsiEBEGVd(+E9Q}OB>oYX>DmPV{g}JNX-lkgQIQ+v!fC? zp3_RT4RyWeuQ8W{Mf$?oXq?YU`Ht?3er=n`t?!-n#jP}Mxv96=s}#7FM&beeWq4qG z+R*1R?p(hFDFuKiuzprVIcnn`)70`{+mdwetjes4qb2esIJ*S4;7Zpbf>>m~!5>&0hN9(o&W|c*kH#0W)DCh5E{$q+5jcOSgOzaL}q-bym#mhx-~` zY!kVA2Zhe>ZcCXQHqoXS;{L1Dryl(W;fhg5g{v7F@PkWZ|Io*&bnuZ|gx+D$s0 zOMdx6_Wa_RfyZb|oCE9=+kSvV4)MADNf&8xJN|zFHs@ zKCQj~eHfDb>iMqOMUn?{njiEOwh5M2E2-nX4*1*V0|6-nR)JXsKE0~}AMCM(GoBBl zZY}PVA;6gYg@vajKc9Y;qfh|Va9s&j+_})Eh4PAYW>-f>Mngy$L7Z}7ka1qq(J;VB z2!261`L28f`H=$jI{lSM9K0p=ZIKQNE`svV56!QgQmfVbs<(e^tgo;_(DtojU~oZA z*ON|b`gWazJ5B-x&*wSS^9&A@%8exmE-5)oDXFn=*R;#UFml3bchAdr-BP`2)`dd# zC#s~RqKuY)7Qv6VS%>*a{OI$AET!ss=-Ir-Lvu7ur7rpxd|Kyqv?v*I7;QLvHF&0$(0=0QFv_62y7L;> zX0`pLMWm^x_$_W>kukolsE%cmN>==n&|`HE1F$vA(1qi$knc50NVOQXVP^CsjF?{L zrxi{hx>BQIM~v&IY0aujuz`@OI7aE;bav{9o#&JFgq}pzH8cW4&sgPoDV)^GpAQN| z5}j|0c-u?V9&#Eh@bA!WrgY~ta(9ex5pmC=k<9Kt@l1l?p21%R8sxLR#~phuDs(Z{ zxC`GDkg-`q!+g9=l>?^@0+>+b9~Syox7OqkZf2Z6sX54?sA3RXMX}r6zjM5~S9r&S zEkkT1Oe3~H8+83YD>;rQn;G6tWB5FB%I3mrpKVDP9Qsi2)h902hP4s6W9 z6E8TW-+1wugWhTa)6%lMvZ`)p*4EUN6i<_XeaT{}(aVfh^rK?0O1TqWY+mU%GsSX6 zwK}B?P0-R_Gu_C>rbb(m@rFG-4lI&^;Lq4$X0LNs^2=Otd}73gwF)V-=NA|Ld2vX# zc6M+W7$FizOn8)xl3VJ+^w(3b8Jz_!RC~oDxU?)REY3)8;6`JqJ&klmb*pn9eSovw zA1>7!Z_H+GZnzHaz<_*;J0`2ww+Y!WB5*i*%W=GVOOz(Zl4rw3tv~8nI1kOI=nTz& zNE~zkF{o)niw^hWD!CJW^0fW(B@Nn3iVRc>i)ynvDyJ4LM>-Df2vLg^@Qja?kX@xx zLs>eR94+eiAJoJT6_8&I%GQsyM@PsQ@L|UEYO2-?gqNL5 z7s48v+M(h-4~A~Ui1xJ3Lb>bjQfaD7h?a*EyN0Jf6ATOM(4|24X^*LNrI-CO{jtBC zN2ji?PUA3%h_>6?R5AT^#Alc9c1Lve)B4ftsF zJEb~0lAV3e9-Plkc=2apyo!#P zA_YGHQ^`0x>J;4A&m4Px#>4))R(in|PfylkfkJ3@r>n5x2PzK@80kND+W9v$8w`26 zgLg`)bF5e_TN_!EyOu+>a7Ydwn4hd@6^}iNXu3-)kK%gSPY|eTi;g}g!`w*O^$)m> z_?@J_Fnw8jh8qGyDWSt*5*xY~A*-)S(|+9MdblKQz^RmvK5Gn4M$se(20x z4s1q80AbsfP6=JqhPH{nok#HyE1Ekw;+z00D#MD!g$GDFBAofj4Y9iulOoyT=3S=M zPxG#@Bc|F}IB-ZTPRLX2)9TAAnyUF0otV*^H;6B2e}y_heN>ZgZZ1lGWmq^zx3rfu zld=e*)3z53R{|k$#;O9%-nG=c4k}{mCo7eu5%#Q78rAXSXEk3aZ_Cx0)-u*YUq}2! zy`xhyh7KJ0iHe%Ks7~v!`MZs27p>3k&F~AHvO7dlF>eETgnhrCUaj5kA)R>rv0v*0ha}^qWw`FVl zv^TN}y~-Gxi{GECBIhJz7%f!9EUij9EHLdESi>yod83$-5zt^Vj5=o*5!+5Dfh_jU zaU#(x{bvz7TdBA6d`v+s?QTtGApqrnWT+)KGi{t zfjL-MSWHye3DKKAQ|_l)PAX&lLq#cEFW zwMj`YQYRJ1_^Fsb&yok^{p?IDFi2Z{*(mXFBa|#~=iOd5pim%Tn~)S?)#duk1~O8=TImLSL!!;SieA?q%em54)z}o8Ys&nbt{NSf zekI@oco|`VFjVncHubVIxrbH$?FDZ;;|RS5&zoePyIIsY)1Rn;=p!?3 zlH*UET&jheLz^a$t=Yt??BEVunp9~+Cv-yZ5;7sen&9mxcQ)`}>eL5mdxHkrGa zTNH!HPCFP&f?rZV@$Yjg2-~Ak5EieAN&8D_`E!#^%D>-ZnN!+s2HP z9C8Tx3aCmKA71Z#oXP-aB9$bJ%*D^y)wQz-@-17+aqrf`T(#o0mRDRq5+pFCIRz5t z(vpUW&ZjO6>JIbhsWa<92Ytnnlze1|3SSkc#T$sAR;)XnW(06nP(bVe;xy6bS5ic& zyt{A~{Jjx!WZw@Vb_=$nqDqdVE@&aT=8Et7VHa_o)=;VzmcfYA?k7oVwF#6(Kl@Zu zU~dnlJwgaN;0WpCa=kH-x~O%HCf{*?O?f?mz-;*1I@`?;oc0>i_G6r*PGt3yZ$zcb zq+frbObsxr$23~oj)%coS+xym>p1#Ro9vS=yf?e-XYauCyeU&~ov${Ek4^-@n~5YNqr3XoGb8Y@$PkFY|rUgQ`~;Gu8 z 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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 extends IEffect { /// /// {@category do_notation} Effect provide(E Function(V env) f) => Effect._( + // ignore: null_check_on_nullable_type_parameter (env) => _unsafeRun(f(env!)), ); /// {@category do_notation} static Effect env() => 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 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 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 implements Exception { } } -EffectGen _effectGen(E? env) => ( +EffectGen _effectGen(E env) => ( async: (effect) => Future.sync( () => effect.asEffect._unsafeRun(env).then( (exit) => switch (exit) { @@ -59,11 +59,7 @@ abstract interface class IEffect { } final class Effect extends IEffect { - /// `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> Function(E? env) _unsafeRun; - + final FutureOr> Function(E env) _unsafeRun; const Effect._(this._unsafeRun); @override @@ -155,10 +151,12 @@ final class Effect extends IEffect { try { return f(_effectGen(env)).then( Right.new, - onError: (dynamic error) { + onError: (error) { if (error is _EffectThrow) { return Left, R>(error.cause); } + + return Left, R>(Die.current(error)); }, ); } on _EffectThrow catch (genError) { @@ -282,14 +280,12 @@ final class Effect extends IEffect { /// /// {@category do_notation} Effect provide(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 env() => Effect._( - // ignore: null_check_on_nullable_type_parameter - (env) => Right(env!), + (env) => Right(env), ); /// {@category combining} @@ -555,31 +551,11 @@ final class Effect extends IEffect { ); } -extension ProvideNever on Effect { - /// Add a required dependency instead of [Never]. +extension ProvideVoid on Effect { + /// Add a required dependency instead of [void]. /// /// {@category do_notation} Effect withEnv() => Effect._( (env) => _unsafeRun(null), ); - - /// {@category execution} - R runSyncNoEnv() => Effect._( - (_) => _unsafeRun(null), - ).runSync(null); - - /// {@category execution} - Exit runSyncExitNoEnv() => Effect._( - (_) => _unsafeRun(null), - ).runSyncExit(null); - - /// {@category execution} - Future runFutureNoEnv() async => Effect._( - (_) => _unsafeRun(null), - ).runFuture(null); - - /// {@category execution} - Future> runFutureExitNoEnv() async => Effect._( - (_) => _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 on FutureOr { - FutureOr then(FutureOr Function(A a) f, {Function? onError}) => + FutureOr then(FutureOr Function(A a) f, + {B Function(Object error)? onError}) => switch (this) { - final Future self => self.then(f, onError: onError), + final Future 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.gen(($) { + final value = $.sync(Effect.succeed(10)); + return value; }); + final result = main.runSync(null); + expect(result, 10); + }); - test('sync fail', () { - final main = Effect.gen(($) { - final value = $.sync(Effect.fail("abc")); - return value; - }); - final result = main.flip().runSyncNoEnv(); - expect(result, "abc"); + test('sync fail', () { + final main = Effect.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.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())); + 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.runSync(null), throwsA(isA())); }); }); }, From 60290a9470fcf5d63cffeef1e446ba226fca4098 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 extends IEffect { ) => value is R ? Right(value) : Left(onError(value)); + static Iterable getRights(Iterable> iterable) sync* { + for (var either in iterable) { + if (either is Right) { + yield either.value; + } + } + } + + static Iterable getLefts(Iterable> iterable) sync* { + for (var either in iterable) { + if (either is Left) { + yield either.value; + } + } + } + R? toNullable(); Option toOption(); Either flatMap(Either 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 on Iterable { /// {@macro fpdart_iterable_extension_head} /// @@ -407,8 +406,16 @@ extension FpdartOnIterable on Iterable { ); } -/// Functional programming functions on `Iterable>` using `fpdart`. extension FpdartOnIterableOfIterable on Iterable> { /// From a `Iterable>` return a `Iterable` of their concatenation. Iterable get flatten => expand(identity); } + +extension FpdartSequenceIterableOption on Iterable> { + Iterable get getSomes => Option.getSomes(this); +} + +extension FpdartSequenceIterableEither on Iterable> { + Iterable get getRights => Either.getRights(this); + Iterable 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 extends IEffect { } } + static Iterable getSomes(Iterable> iterable) sync* { + for (var option in iterable) { + if (option is Some) { + yield option.value; + } + } + } + R? toNullable(); Option flatMap(Option Function(R r) f); From d3307685dc531794f6bbd71f26fd1c9b7f71d83c Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 extends IEffect { ._unsafeRun(env), ), ); + + /// {@category filtering} + Effect filterOrDie({ + 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 filterOrElse({ + required bool Function(R r) predicate, + required Effect 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 on Effect { From 8ad0f09ba3049a55ff8521b70320f0b989e3f1c5 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 extends IEffect { } } + factory Option.fromJson( + dynamic json, + R Function(dynamic json) fromJson, + ) => + json != null ? Option.tryCatch(() => fromJson(json)) : None(); + static Iterable getSomes(Iterable> iterable) sync* { for (var option in iterable) { if (option is Some) { @@ -37,8 +43,13 @@ sealed class Option extends IEffect { } } + Object? toJson(Object? Function(R value) toJson); R? toNullable(); Option flatMap(Option Function(R r) f); + Option andThen(C Function(R r) f); + Option tap(Option Function(R r) f); + Option filter(bool Function(R r) f); + Option filterMap(Option Function(R r) f); Option ap( Option f, @@ -74,17 +85,21 @@ final class Some extends Option { @override Effect provide(L Function() onNone) => Effect.succeed(value); - Option andThen(Option Function() then) => then(); - @override Option flatMap(Option Function(R r) f) => f(value); @override R toNullable() => value; + @override + Option andThen(C Function(R r) f) => Some(f(value)); + @override Either toEither(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 extends Option { @override String toString() => 'Some($value)'; + + @override + Option tap(Option Function(R r) f) => f(value).map((_) => value); + + @override + Option filter(bool Function(R r) f) { + if (f(value)) return Some(value); + return None(); + } + + @override + Option filterMap(Option Function(R r) f) { + if (f(value) case Some(value: final value)) return Some(value); + return None(); + } } final class None extends Option { @@ -106,14 +136,27 @@ final class None extends Option { // ignore: cast_from_null_always_fails Effect._((_) => Left(Fail(null as Never))); - Option andThen(Option Function() then) => this; - @override Option flatMap(Option Function(Never r) f) => this; @override Null toNullable() => null; + @override + Object? toJson(Object? Function(Never value) toJson) => None(); + @override String toString() => 'None'; + + @override + Option andThen(C Function(Never r) f) => None(); + + @override + Option tap(Option Function(Never r) f) => None(); + + @override + Option filter(bool Function(Never r) f) => None(); + + @override + Option filterMap(Option Function(Never r) f) => None(); } From b73849d4800db4fe40672c429d4558d9124bac46 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 extends IEffect { } } - R? toNullable(); - Option toOption(); + R? getOrNull(); Either flatMap(Either Function(R r) f); Either mapLeft(C Function(L l) f); Effect provide(); @@ -63,8 +62,17 @@ sealed class Either extends IEffect { required D Function(L l) onLeft, required C Function(R r) onRight, }); - Either get flip; + Either flip(); R getOrElse(R Function(L l) orElse); + Either andThen(C Function(R r) f); + Either orElse(Either Function(L l) orElse); + Either tap(Either Function(R r) f); + Either filterOrLeft({ + required bool Function(R r) predicate, + required L Function(R r) orLeftWith, + }); + Option getLeft(); + Option getRight(); Either ap( Either f, @@ -85,15 +93,17 @@ final class Right extends Either { @override Effect get asEffect => Effect._((_) => Right(value)); - Either andThen(Either Function() then) => then(); + @override + Either andThen(C Function(R value) f) => Right(f(value)); + @override Either orElse(Either Function(L l) orElse) => Right(value); @override R getOrElse(R Function(L l) orElse) => value; @override - Either get flip => Left(value); + Either flip() => Left(value); @override Either mapBoth( @@ -111,10 +121,10 @@ final class Right extends Either { Effect provide() => Effect.succeed(value); @override - R toNullable() => value; + R getOrNull() => value; @override - Option toOption() => Some(value); + Option getRight() => Some(value); @override bool operator ==(Object other) => (other is Right) && other.value == value; @@ -124,6 +134,20 @@ final class Right extends Either { @override String toString() => 'Right($value)'; + + @override + Either filterOrLeft({ + required bool Function(R r) predicate, + required L Function(R r) orLeftWith, + }) => + predicate(value) ? Right(value) : Left(orLeftWith(value)); + + @override + Option getLeft() => None(); + + @override + Either tap(Either Function(R r) f) => + f(value).map((_) => value); } final class Left extends Either { @@ -133,15 +157,17 @@ final class Left extends Either { @override Effect get asEffect => Effect._((_) => Left(Fail(value))); - Either andThen(Either Function() then) => Left(value); + @override + Either andThen(C Function(R value) f) => Left(value); + @override Either orElse(Either Function(L l) orElse) => orElse(value); @override R getOrElse(R Function(L l) orElse) => orElse(value); @override - Either get flip => Right(value); + Either flip() => Right(value); @override Either mapBoth( @@ -159,10 +185,10 @@ final class Left extends Either { Effect provide() => Effect.fail(value); @override - R? toNullable() => null; + R? getOrNull() => null; @override - Option toOption() => None(); + Option getRight() => None(); @override bool operator ==(Object other) => (other is Left) && other.value == value; @@ -172,4 +198,17 @@ final class Left extends Either { @override String toString() => 'Left($value)'; + + @override + Either filterOrLeft({ + required bool Function(R r) predicate, + required L Function(R r) orLeftWith, + }) => + Left(value); + + @override + Option getLeft() => Some(value); + + @override + Either tap(Either 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 extends IEffect { } Object? toJson(Object? Function(R value) toJson); - R? toNullable(); + R? getOrNull(); Option flatMap(Option Function(R r) f); Option andThen(C Function(R r) f); Option tap(Option Function(R r) f); @@ -89,7 +89,7 @@ final class Some extends Option { Option flatMap(Option Function(R r) f) => f(value); @override - R toNullable() => value; + R getOrNull() => value; @override Option andThen(C Function(R r) f) => Some(f(value)); @@ -140,7 +140,7 @@ final class None extends Option { Option flatMap(Option 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 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 extends IEffect { 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 extends IEffect { /// {@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 extends IEffect { ); /// {@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 extends IEffect { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail(error: final error) => Right(Left(error)), + Failure(error: final error) => Right(Left(error)), Die() => Left(cause), }, Right(value: final value) => Right(Right(value)), @@ -316,7 +316,7 @@ final class Effect extends IEffect { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail() => Right(None()), + Failure() => Right(None()), Die() => Left(cause), }, Right(value: final value) => Right(Some(value)), @@ -340,7 +340,7 @@ final class Effect extends IEffect { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail(error: final error) => Right(onFailure(error)), + Failure(error: final error) => Right(onFailure(error)), Die() => Left(cause), }, Right(value: final value) => Right(onSuccess(value)), @@ -371,7 +371,8 @@ final class Effect extends IEffect { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail(error: final error) => onFailure(error)._unsafeRun(env), + Failure(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 extends IEffect { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail(error: final error) => Right(error), + Failure(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 extends IEffect { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail(error: final error) => Left(Fail(f(error))), + Failure(error: final error) => Left(Failure(f(error))), Die() => Left(cause), }, Right(value: final value) => Right(value), @@ -426,7 +427,7 @@ final class Effect extends IEffect { Effect mapErrorCause(C Function(Cause 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 extends IEffect { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail(error: final error) => Left(Fail(fl(error))), + Failure(error: final error) => Left(Failure(fl(error))), Die() => Left(cause), }, Right(value: final value) => Right(fr(value)), @@ -465,8 +466,8 @@ final class Effect extends IEffect { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail(error: final error) => f(error)._unsafeRun(env).then( - (_) => Left(Fail(error)), + Failure(error: final error) => f(error)._unsafeRun(env).then( + (_) => Left(Failure(error)), ), Die() => Left(cause), }, @@ -483,7 +484,7 @@ final class Effect extends IEffect { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail(error: final error) => orElse(error)._unsafeRun(env), + Failure(error: final error) => orElse(error)._unsafeRun(env), Die() => Left(cause), }, Right(value: final value) => @@ -509,7 +510,7 @@ final class Effect extends IEffect { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail(error: final error) => + Failure(error: final error) => Left(Die.current(onError(error))), Die() => Left(cause), }, @@ -527,7 +528,7 @@ final class Effect extends IEffect { (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Fail(error: final error) => f(error)._unsafeRun(env), + Failure(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 extends Either { const Left(this.value); @override - Effect get asEffect => Effect._((_) => Left(Fail(value))); + Effect get asEffect => Effect._((_) => Left(Failure(value))); @override Either andThen(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 { 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 { } /// Failed with an expected error -final class Fail extends Cause { +final class Failure extends Cause { 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 extends IEffect { Effect provide(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 { @override Effect get asEffect => // ignore: cast_from_null_always_fails - Effect._((_) => Left(Fail(null as Never))); + Effect._((_) => Left(Failure(null as Never))); @override Option flatMap(Option Function(Never r) f) => this; From 8a0f930c8aa8f007ebed30b56db797ac5064ca27 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 on FutureOr { FutureOr then(FutureOr Function(A a) f, - {B Function(Object error)? onError}) => - switch (this) { - final Future self => self.then(f, onError: (Object error) { + {B Function(Object error)? onError}) { + switch (this) { + case Future 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 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 extends IEffect { try { return f(_effectGen(env)).then( Right.new, - onError: (error) { + onError: (error, stackTrace) { if (error is _EffectThrow) { return Left, R>(error.cause); } - return Left, R>(Die.current(error)); + return Left, R>(Die(error, stackTrace)); }, ); } on _EffectThrow catch (genError) { @@ -174,7 +175,12 @@ final class Effect extends IEffect { 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 on FutureOr { FutureOr then(FutureOr Function(A a) f, - {B Function(Object error)? onError}) { + {B Function(Object error, StackTrace stackTrace)? onError}) { switch (this) { case Future 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 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 extends IEffect { (_) => f().then(Right.new), ); + /// {@category constructors} + factory Effect.from(Exit Function(E env) f) => Effect._(f); + /// {@category constructors} factory Effect.fail(L value) => Effect._((_) => Left(Failure(value))); @@ -282,13 +285,14 @@ final class Effect extends IEffect { ) => flatMap((_) => effect); - /// Extract the required dependency from the complete environment. - /// /// {@category do_notation} - Effect provide(E Function(V env) f) => Effect._( + Effect mapEnv(E Function(V env) f) => Effect._( (env) => _unsafeRun(f(env)), ); + /// {@category do_notation} + Effect provide(E env) => Effect._((_) => _unsafeRun(env)); + /// {@category do_notation} static Effect env() => Effect._( (env) => Right(env), @@ -589,11 +593,21 @@ final class Effect extends IEffect { ); } -extension ProvideVoid on Effect { - /// Add a required dependency instead of [void]. - /// +extension ProvideNull on Effect { /// {@category do_notation} Effect withEnv() => Effect._( (env) => _unsafeRun(null), ); + + /// {@category execution} + R runSyncVoid() => runSync(null); + + /// {@category execution} + Future runFutureVoid() => runFuture(null); + + /// {@category execution} + Either, R> runSyncExitVoid() => runSyncExit(null); + + /// {@category execution} + Future, 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.gen(($) { + final main = Effect.gen(($) { final value = $.sync(Effect.succeed(10)); return value; }); @@ -62,7 +62,7 @@ void main() { }); test('sync fail', () { - final main = Effect.gen(($) { + final main = Effect.gen(($) { final value = $.sync(Effect.fail("abc")); return value; }); @@ -71,7 +71,7 @@ void main() { }); test('async succeed', () async { - final main = Effect.gen(($) async { + final main = Effect.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.gen(($) { + final main = Effect.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.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.from((env) => Right(env + 1)); + final main = Effect.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 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 extends IEffect { /// {@category do_notation} Effect provide(E env) => Effect._((_) => _unsafeRun(env)); + /// {@category do_notation} + Effect provideEffect(Effect 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 env() => 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.gen(($) { + final env = $.sync(Effect.env()); + return env + 1; + }); + + final program = main.provideEffect(Effect.succeed(10)); + final result = program.runSyncVoid(); + expect(result, 11); + }); + + test('invalid dependency', () { + final main = Effect.gen(($) { + final env = $.sync(Effect.env()); + return env + 1; + }); + + final program = main.provideEffect(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 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(Effect.succeed(10)); + final program = + main.provideEffect(Effect.succeed(10)); final result = program.runSyncVoid(); expect(result, 11); }); @@ -36,7 +37,8 @@ void main() { return env + 1; }); - final program = main.provideEffect(Effect.fail("error")); + final program = + main.provideEffect(Effect.fail("error")); final result = program.flip().runSyncVoid(); expect(result, "error"); }); From dcb98619043e5271e1c459fe63d2e009b10b8dae Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 extends IEffect { 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 { - rethrow; - } catch (error, stackTrace) { - throw Die(error, stackTrace); - } - } - - /// {@category execution} - Exit 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 catch (cause) { - return Left(cause); - } catch (error, stackTrace) { - return Left(Die(error, stackTrace)); - } - } - - /// {@category execution} - Future 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 { - rethrow; - } catch (error, stackTrace) { - throw Die(error, stackTrace); - } - } - - /// {@category execution} - Future> runFutureExit(E env) async { - try { - final result = _unsafeRun(env); - if (result is! Future) { - return result; - } - return result; - } on Cause catch (cause) { - return Left(cause); - } catch (error, stackTrace) { - return Left(Die(error, stackTrace)); - } - } - /// {@category constructors} factory Effect.gen(DoFunctionEffect f) => Effect._( (env) { @@ -610,14 +535,77 @@ extension ProvideNull on Effect { ); /// {@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 { + rethrow; + } catch (error, stackTrace) { + throw Die(error, stackTrace); + } + } /// {@category execution} - Future runFutureVoid() => runFuture(null); + Exit runSyncExit() { + try { + final result = _unsafeRun(null); + if (result is Future) { + return Left(Die.current( + Exception("runSyncExit cannot execute async Effect"), + )); + } + return result; + } on Cause catch (cause) { + return Left(cause); + } catch (error, stackTrace) { + return Left(Die(error, stackTrace)); + } + } /// {@category execution} - Either, R> runSyncExitVoid() => runSyncExit(null); + Future 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 { + rethrow; + } catch (error, stackTrace) { + throw Die(error, stackTrace); + } + } /// {@category execution} - Future, R>> runFutureExitVoid() => runFutureExit(null); + Future> runFutureExit() async { + try { + final result = _unsafeRun(null); + if (result is! Future) { + return result; + } + return result; + } on Cause 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.succeed(10).orDie; + final result = main.runSync(); expect(result, 10); }); test('fail', () { - final main = Effect.fail(10).orDie; - expect(() => main.runSync(null), throwsA(isA())); + final main = Effect.fail("error").orDie; + expect(() => main.runSync(), throwsA(isA())); }); }); group('orDieWith', () { test('succeed', () { - final main = Effect.succeed(10).orDieWith((_) => CustomError()); - final result = main.runSync(null); + final main = Effect.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())); + final main = Effect.fail("error") + .orDieWith((_) => CustomError()); + expect(() => main.runSync(), throwsA(isA())); }); }); }, 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([ 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([ + final main = Effect.all([ 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.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.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.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.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.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())); + expect(() => main.runSync(), throwsA(isA())); }); }); }, 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.succeed(10)); - final result = program.runSyncVoid(); + final result = program.runSync(); expect(result, 11); }); @@ -39,7 +39,7 @@ void main() { final program = main.provideEffect(Effect.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.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.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.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 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 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 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 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 get(Uri uri); + Effect get(Uri uri); } class Http implements HttpClient { @override - Effect get(Uri uri) => Effect.gen( + Effect get(Uri uri) => Effect.gen( ($) async { final response = await $.async(Effect.tryCatch( execute: () => http.get(uri), @@ -42,7 +42,7 @@ Effect 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 on FutureOr { - FutureOr then(FutureOr Function(A a) f, - {B Function(Object error, StackTrace stackTrace)? onError}) { + FutureOr then( + FutureOr Function(A a) f, { + B Function(Object error, StackTrace stackTrace)? onError, + }) { switch (this) { case Future 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 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 = FutureOr Function( EffectGen $, ); -abstract interface class IEffect { +abstract class IEffect { const IEffect(); Effect get asEffect; } From 34730962b3c4000fa4ed1da93c830d0c0207451c Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 on FutureOr { 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 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 extends IEffect { return "Effect(${_unsafeRun.runtimeType})"; } + /// {@category constructors} + factory Effect.from(FutureOr> Function(E env) run) => + Effect._((env) { + try { + return run(env); + } on Cause catch (cause) { + return Left(cause); + } catch (error, stackTrace) { + return Left(Die(error, stackTrace)); + } + }); + /// {@category constructors} factory Effect.gen(DoFunctionEffect f) => Effect._( (env) { @@ -97,7 +109,7 @@ final class Effect extends IEffect { required L Function(Object error, StackTrace stackTrace) onError, FutureOr Function()? onCancel, }) => - Effect._( + Effect.from( (env) { try { return execute().then( @@ -114,42 +126,39 @@ final class Effect extends IEffect { /// {@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> Function() f) => Effect._( + factory Effect.functionFail(FutureOr> Function() f) => Effect.from( (_) => f().then(Left.new), ); /// {@category constructors} - factory Effect.functionSucceed(FutureOr Function() f) => Effect._( + factory Effect.functionSucceed(FutureOr Function() f) => Effect.from( (_) => f().then(Right.new), ); /// {@category constructors} - factory Effect.from(Exit 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 die(dynamic defect) => Effect._( + static Effect die(dynamic defect) => Effect.from( (_) => Left(Die.current(defect)), ); /// {@category constructors} static Effect functionDie(dynamic Function() run) => - Effect._( + Effect.from( (_) => Left(Die.current(run())), ); /// {@category constructors} - static Effect unit() => Effect._( + static Effect unit() => Effect.from( (_) => const Right(fpdart_unit.unit), ); @@ -158,7 +167,7 @@ final class Effect extends IEffect { Iterable iterable, Effect Function(A a, int index) f, ) => - Effect._( + Effect.from( (env) { if (iterable.isEmpty) { return const Right([]); @@ -211,15 +220,15 @@ final class Effect extends IEffect { flatMap((_) => effect); /// {@category do_notation} - Effect mapEnv(E Function(V env) f) => Effect._( + Effect mapEnv(E Function(V env) f) => Effect.from( (env) => _unsafeRun(f(env)), ); /// {@category do_notation} - Effect provide(E env) => Effect._((_) => _unsafeRun(env)); + Effect provide(E env) => Effect.from((_) => _unsafeRun(env)); /// {@category do_notation} - Effect provideEffect(Effect effect) => Effect._( + Effect provideEffect(Effect effect) => Effect.from( (env) => effect._unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => Left(cause), @@ -229,7 +238,7 @@ final class Effect extends IEffect { ); /// {@category do_notation} - static Effect env() => Effect._( + static Effect env() => Effect.from( (env) => Right(env), ); @@ -244,7 +253,7 @@ final class Effect extends IEffect { ); /// {@category conversions} - Effect> either() => Effect._( + Effect> either() => Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -257,7 +266,7 @@ final class Effect extends IEffect { ); /// {@category conversions} - Effect> option() => Effect._( + Effect> option() => Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -270,7 +279,7 @@ final class Effect extends IEffect { ); /// {@category conversions} - Effect> exit() => Effect._( + Effect> exit() => Effect.from( (env) => _unsafeRun(env).then( (exit) => Right(exit), ), @@ -281,7 +290,7 @@ final class Effect extends IEffect { 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 extends IEffect { required C Function(Cause 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 extends IEffect { required Effect Function(L l) onFailure, required Effect 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 extends IEffect { required Effect Function(Cause l) onFailure, required Effect 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 extends IEffect { ); /// {@category mapping} - Effect flip() => Effect._( + Effect flip() => Effect.from( (env) => _unsafeRun(env).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { @@ -356,7 +365,7 @@ final class Effect extends IEffect { Effect map(V Function(R r) f) => ap(Effect.succeed(f)); /// {@category mapping} - Effect mapError(C Function(L l) f) => Effect._( + Effect mapError(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 extends IEffect { ); /// {@category mapping} - Effect mapErrorCause(C Function(Cause l) f) => Effect._( + Effect mapErrorCause(C Function(Cause 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 extends IEffect { /// {@category mapping} Effect mapBoth(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 extends IEffect { ); /// {@category sequencing} - Effect flatMap(Effect Function(R r) f) => Effect._( + Effect flatMap(Effect 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 extends IEffect { flatMap((r) => f(r).map((_) => r)); /// {@category sequencing} - Effect tapError(Effect Function(L l) f) => Effect._( + Effect tapError(Effect 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 extends IEffect { Effect orElse( Effect 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 extends IEffect { ); /// {@category alternatives} - Effect get orDie => Effect._( + Effect 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 extends IEffect { /// {@category alternatives} Effect orDieWith(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 extends IEffect { Effect catchError( Effect 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 extends IEffect { Effect catchCause( Effect Function(Cause 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 extends IEffect { 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 extends IEffect { required bool Function(R r) predicate, required Effect 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 extends IEffect { extension ProvideNull on Effect { /// {@category do_notation} - Effect withEnv() => Effect._( + Effect withEnv() => 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.functionSucceed(() { + throw "fail"; + }) + .catchCause( + (cause) => Effect.succeed("abc"), + ) + .runSync(); + + expect(result, "abc"); + }); + }); + }, + ); +} From 83667b4c3993ada1655622a4f8aec008fc40e734 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 { + final E env; + final Deferred signal; + + const Context._({required this.env, required this.signal}); + + factory Context({ + required E env, + required Deferred signal, + }) => + Context._(env: env, signal: signal); + + factory Context.env(E env) => Context._(env: env, signal: Deferred()); + + Context withEnv(C env) => Context._(env: env, signal: signal); + + Context get withoutSignal => withSignal(Deferred()); + Context withSignal(Deferred signal) => + copyWith(signal: signal); + + Context copyWith({ + E? env, + Deferred? 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 extends IEffect { + Option> _value = None(); + + Completer>? __completer; + + Deferred(); + + Deferred.completed(R value) : _value = Some(Right(value)); + Deferred.failedCause(Cause cause) : _value = Some(Left(cause)); + Deferred.failed(L error) : _value = Some(Left(Failure(error))); + + Completer> get _completer => + __completer ??= Completer>.sync(); + + bool get unsafeCompleted => _value is Some>; + + @override + Effect get asEffect => Effect.from( + (_) => await()._unsafeRun(Context.env(null)), + ); + + Effect await() => Effect.from( + (ctx) async => switch (_value) { + None() => await _completer.future, + Some(value: final value) => value, + }, + ); + + void unsafeCompleteExit(Exit exit) { + if (_value is Some>) return; + + _value = Some(exit); + __completer?.complete(exit); + } + + Effect completeExit(Exit exit) => + Effect.functionSucceed(() { + switch (_value) { + case None(): + unsafeCompleteExit(exit); + return unit; + case Some(): + return unit; + } + }); + + Effect failCause(Cause 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 implements Exception { } } -EffectGen _effectGen(E env) => ( +EffectGen _effectGen(Context context) => ( async: (effect) => Future.sync( - () => effect.asEffect._unsafeRun(env).then( + () => effect.asEffect._unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => throw _EffectThrow(cause), Right(value: final value) => value, @@ -34,7 +36,7 @@ EffectGen _effectGen(E env) => ( ), ), sync: (effect) { - final run = effect.asEffect._unsafeRun(env); + final run = effect.asEffect._unsafeRun(context); if (run is Future) { throw _EffectThrow( Die.current( @@ -60,7 +62,7 @@ abstract class IEffect { } final class Effect extends IEffect { - final FutureOr> Function(E env) _unsafeRun; + final FutureOr> Function(Context context) _unsafeRun; const Effect._(this._unsafeRun); @override @@ -71,11 +73,29 @@ final class Effect extends IEffect { return "Effect(${_unsafeRun.runtimeType})"; } + FutureOr> __unsafeRun(Context 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> Function(E env) run) => - Effect._((env) { + factory Effect.from(FutureOr> Function(Context context) run) => + Effect._((context) { try { - return run(env); + return run(context); } on Cause catch (cause) { return Left(cause); } catch (error, stackTrace) { @@ -146,6 +166,30 @@ final class Effect extends IEffect { /// {@category constructors} factory Effect.succeed(R value) => Effect.from((_) => Right(value)); + /// {@category constructors} + factory Effect.raceAll(Iterable> iterable) => + Effect.from((context) { + final signal = Deferred(); + final deferred = Deferred(); + + for (final effect in iterable) { + effect + .__unsafeRun(context.withSignal(signal)) + .then(deferred.unsafeCompleteExit); + + if (deferred.unsafeCompleted) { + break; + } + } + + return deferred.await().__unsafeRun(context).then( + (exit) => signal + .failCause(const Interrupted()) + .__unsafeRun(context.withoutSignal) + .then((_) => exit), + ); + }); + /// {@category constructors} static Effect die(dynamic defect) => Effect.from( (_) => Left(Die.current(defect)), @@ -168,7 +212,7 @@ final class Effect extends IEffect { Effect Function(A a, int index) f, ) => Effect.from( - (env) { + (context) { if (iterable.isEmpty) { return const Right([]); } @@ -182,7 +226,7 @@ final class Effect extends IEffect { (list, r) => list.append(r), ), ) - ._unsafeRun(env); + ._unsafeRun(context); }, ); @@ -220,26 +264,28 @@ final class Effect extends IEffect { flatMap((_) => effect); /// {@category do_notation} - Effect mapEnv(E Function(V env) f) => Effect.from( - (env) => _unsafeRun(f(env)), + Effect mapEnv(Context Function(Context context) f) => + Effect.from( + (context) => _unsafeRun(f(context)), ); /// {@category do_notation} - Effect provide(E env) => Effect.from((_) => _unsafeRun(env)); + Effect provide(E env) => + Effect.from((_) => _unsafeRun(Context.env(env))); /// {@category do_notation} Effect provideEffect(Effect 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 env() => Effect.from( - (env) => Right(env), + (context) => Right(context.env), ); /// {@category combining} @@ -254,11 +300,12 @@ final class Effect extends IEffect { /// {@category conversions} Effect> either() => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure(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 extends IEffect { /// {@category conversions} Effect> option() => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure() => Right(None()), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => Right(Some(value)), }, @@ -280,7 +328,7 @@ final class Effect extends IEffect { /// {@category conversions} Effect> exit() => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => Right(exit), ), ); @@ -291,11 +339,12 @@ final class Effect extends IEffect { 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(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 extends IEffect { 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 extends IEffect { required Effect Function(R r) onSuccess, }) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure(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 extends IEffect { required Effect 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 flip() => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { Failure(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 extends IEffect { /// {@category mapping} Effect mapError(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(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 extends IEffect { /// {@category mapping} Effect mapErrorCause(C Function(Cause 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 extends IEffect { /// {@category mapping} Effect mapBoth(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(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 extends IEffect { /// {@category sequencing} Effect flatMap(Effect 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 extends IEffect { /// {@category sequencing} Effect tapError(Effect Function(L l) f) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Failure(error: final error) => f(error)._unsafeRun(env).then( - (_) => Left(Failure(error)), - ), + Failure(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 extends IEffect { Effect Function(L l) orElse, ) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Failure(error: final error) => orElse(error)._unsafeRun(env), + Failure(error: final error) => + orElse(error)._unsafeRun(context), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => - Effect.succeed(value)._unsafeRun(env), + Effect.succeed(value)._unsafeRun(context), }, ), ); /// {@category alternatives} Effect 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.succeed(value)._unsafeRun(env), + Effect.succeed(value)._unsafeRun(context), }, ), ); @@ -461,15 +518,16 @@ final class Effect extends IEffect { /// {@category alternatives} Effect orDieWith(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(error: final error) => Left(Die.current(onError(error))), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => - Effect.succeed(value)._unsafeRun(env), + Effect.succeed(value)._unsafeRun(context), }, ), ); @@ -479,14 +537,15 @@ final class Effect extends IEffect { Effect Function(L error) f, ) => Effect.from( - (env) => _unsafeRun(env).then( + (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Failure(error: final error) => f(error)._unsafeRun(env), + Failure(error: final error) => f(error)._unsafeRun(context), Die() => Left(cause), + Interrupted() => Left(cause), }, Right(value: final value) => - Effect.succeed(value)._unsafeRun(env), + Effect.succeed(value)._unsafeRun(context), }, ), ); @@ -496,12 +555,12 @@ final class Effect extends IEffect { Effect Function(Cause 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.succeed(value), } - ._unsafeRun(env), + ._unsafeRun(context), ), ); @@ -511,7 +570,7 @@ final class Effect extends IEffect { 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 extends IEffect { required Effect 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 extends IEffect { extension ProvideNull on Effect { /// {@category do_notation} Effect withEnv() => 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 on Effect { /// {@category execution} Exit 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 on Effect { /// {@category execution} Future 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 on Effect { /// {@category execution} Future> runFutureExit() async { try { - final result = _unsafeRun(null); - if (result is! Future) { - return result; - } - return result; + return _unsafeRun(Context.env(null)); } on Cause 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 extends Cause { return "Cause.Fail($error)"; } } + +final class Interrupted extends Cause { + @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.from((env) => Right(env + 1)); + final subMain = Effect.from( + (context) => Right(context.env + 1)); final main = Effect.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 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 extends IEffect { ); }); + /// {@category constructors} + static Effect sleep(Duration duration) => Effect.from( + (_) => Future.delayed( + duration, + () => const Right(null), + ), + ); + /// {@category constructors} static Effect die(dynamic defect) => Effect.from( (_) => Left(Die.current(defect)), @@ -454,6 +462,10 @@ final class Effect extends IEffect { ), ); + /// {@category sequencing} + Effect race(Effect effect) => + Effect.raceAll([this, effect]); + /// {@category sequencing} Effect flatMap(Effect 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(Duration(milliseconds: 50)).map((_) => 1); + final second = + Effect.sleep(Duration(milliseconds: 100)).map((_) => 2); + + final result = await first.race(second).runFuture(); + expect(result, 1); + }); + + test('second wins', () async { + final first = + Effect.sleep(Duration(milliseconds: 100)).map((_) => 1); + final second = + Effect.sleep(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 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 extends IEffect { /// {@category constructors} factory Effect.fail(L value) => Effect.from((_) => Left(Failure(value))); + /// {@category constructors} + factory Effect.failCause(Cause cause) => Effect.from((_) => Left(cause)); + /// {@category constructors} factory Effect.succeed(R value) => Effect.from((_) => Right(value)); @@ -191,7 +194,7 @@ final class Effect extends IEffect { }); /// {@category constructors} - static Effect sleep(Duration duration) => Effect.from( + static Effect sleep(Duration duration) => Effect.from( (_) => Future.delayed( duration, () => const Right(null), @@ -607,6 +610,14 @@ final class Effect extends IEffect { }, ), ); + + /// {@category delay} + Effect delay(Duration duration) => + Effect.sleep(duration).zipRight(this); + + /// {@category delay} + Effect timeout(Duration duration) => + race(Effect.failCause(const Interrupted()).delay(duration)); } extension ProvideNull on Effect { 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(Duration(milliseconds: 50)).map((_) => 1); - final second = - Effect.sleep(Duration(milliseconds: 100)).map((_) => 2); + final first = Effect.sleep(Duration(milliseconds: 50)) + .map((_) => 1); + final second = Effect.sleep(Duration(milliseconds: 100)) + .map((_) => 2); final result = await first.race(second).runFuture(); expect(result, 1); }); test('second wins', () async { - final first = - Effect.sleep(Duration(milliseconds: 100)).map((_) => 1); - final second = - Effect.sleep(Duration(milliseconds: 50)).map((_) => 2); + final first = Effect.sleep(Duration(milliseconds: 100)) + .map((_) => 1); + final second = Effect.sleep(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 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.gen(($) { final eitherValue = $.sync(either); final optionValue = $.sync(option); + final deferred = $.sync(Deferred()); 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 extends IEffect { +final class Deferred extends IEffect> { Option> _value = None(); Completer>? __completer; @@ -17,8 +17,14 @@ final class Deferred extends IEffect { bool get unsafeCompleted => _value is Some>; @override - Effect get asEffect => Effect.from( - (_) => await()._unsafeRun(Context.env(null)), + Effect> 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 await() => 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 extends IEffect { /// {@category delay} Effect timeout(Duration duration) => race(Effect.failCause(const Interrupted()).delay(duration)); + + /// {@category interruption} + Effect interrupt() => Effect.failCause(const Interrupted()); } extension ProvideNull on Effect { From da0b2ee77426e7b4c8616109bfea7dc9b3575c0f Mon Sep 17 00:00:00 2001 From: SandroMaglione 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(10); final option = Some(10); -final effect = Effect.gen(($) { +final effect = Effect.gen(($) async { final eitherValue = $.sync(either); final optionValue = $.sync(option); - final deferred = $.sync(Deferred()); + final deferred = $.sync(Deferred.make().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 { }) => 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 withEnv(C env) => Context._(env: env, signal: signal); - Context get withoutSignal => withSignal(Deferred()); + Context get withoutSignal => withSignal(Deferred.unsafeMake()); Context withSignal(Deferred 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 extends IEffect> { - Option> _value = None(); - +final class Deferred { + Option> _state = None(); Completer>? __completer; - Deferred(); + Deferred._(); + Deferred.unsafeMake(); + + static Effect> make() => + Effect.succeed(Deferred._()); - Deferred.completed(R value) : _value = Some(Right(value)); - Deferred.failedCause(Cause 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 cause) : _state = Some(Left(cause)); + Deferred.failed(L error) : _state = Some(Left(Failure(error))); Completer> get _completer => __completer ??= Completer>.sync(); - bool get unsafeCompleted => _value is Some>; - - @override - Effect> 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>; - Effect await() => Effect.from( - (ctx) async => switch (_value) { + Effect future() => Effect.from( + (ctx) async => switch (_state) { None() => await _completer.future, Some(value: final value) => value, }, ); void unsafeCompleteExit(Exit exit) { - if (_value is Some>) return; + if (_state is Some>) return; - _value = Some(exit); + _state = Some(exit); __completer?.complete(exit); } Effect completeExit(Exit 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 extends IEffect { /// {@category constructors} factory Effect.raceAll(Iterable> iterable) => Effect.from((context) { - final signal = Deferred(); - final deferred = Deferred(); + final signal = Deferred.unsafeMake(); + final deferred = Deferred.unsafeMake(); for (final effect in iterable) { effect @@ -185,7 +185,7 @@ final class Effect extends IEffect { } } - return deferred.await().__unsafeRun(context).then( + return deferred.future().__unsafeRun(context).then( (exit) => signal .failCause(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.gen(($) async { + final deferred = $.sync(Deferred.make()); + await $.async(Effect.raceAll([ + Effect.sleep(Duration(milliseconds: 100)) + .zipRight(deferred.completeExit(Right(1))), + Effect.sleep(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 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.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()); + } + }); + }); + }, + ); +} From 4c893ef6d567a7099df74b8311e5b798b257050a Mon Sep 17 00:00:00 2001 From: SandroMaglione 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.gen(($) async { final eitherValue = $.sync(either); final optionValue = $.sync(option); final deferred = $.sync(Deferred.make().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 { bool get unsafeCompleted => _state is Some>; - Effect future() => Effect.from( + Effect wait() => 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 extends IEffect { } } - return deferred.future().__unsafeRun(context).then( + return deferred.wait().__unsafeRun(context).then( (exit) => signal .failCause(const Interrupted()) .__unsafeRun(context.withoutSignal) @@ -274,17 +274,28 @@ final class Effect extends IEffect { ) => flatMap((_) => effect); - /// {@category do_notation} - Effect mapEnv(Context Function(Context context) f) => + /// {@category context} + Effect mapContext(Context Function(Context context) f) => Effect.from( (context) => _unsafeRun(f(context)), ); - /// {@category do_notation} - Effect provide(E env) => + /// {@category context} + Effect mapEnv(E Function(V env) f) => Effect.from( + (context) => _unsafeRun( + Context(env: f(context.env), signal: context.signal), + ), + ); + + /// {@category context} + Effect provide(Context context) => + Effect.from((_) => _unsafeRun(context)); + + /// {@category context} + Effect provideEnv(E env) => Effect.from((_) => _unsafeRun(Context.env(env))); - /// {@category do_notation} + /// {@category context} Effect provideEffect(Effect effect) => Effect.from( (context) => effect._unsafeRun(context).then( (exit) => switch (exit) { @@ -294,7 +305,7 @@ final class Effect extends IEffect { ), ); - /// {@category do_notation} + /// {@category context} static Effect env() => Effect.from( (context) => Right(context.env), ); @@ -624,7 +635,7 @@ final class Effect extends IEffect { } extension ProvideNull on Effect { - /// {@category do_notation} + /// {@category context} Effect withEnv() => 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.from( (context) => Right(context.env + 1)); final main = Effect.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 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 @@ -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 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 { - Option> _state = None(); + Option> _state = const None(); Completer>? __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 extends IEffect { (context) => _unsafeRun(context).then( (exit) => switch (exit) { Left(value: final cause) => switch (cause) { - Failure() => Right(None()), + Failure() => Right, Option>(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 extends Either { predicate(value) ? Right(value) : Left(orLeftWith(value)); @override - Option getLeft() => None(); + Option getLeft() => const None(); @override Either tap(Either Function(R r) f) => @@ -188,7 +188,7 @@ final class Left extends Either { R? getOrNull() => null; @override - Option getRight() => None(); + Option 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 on Iterable { Option 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 on Iterable { /// **Note**: Because accessing the last element of an [Iterable] requires /// stepping through all the other elements, `lastOption` **can be slow**. Option get lastOption { - if (isEmpty) return None(); + if (isEmpty) return const None(); return Some(last); } @@ -46,7 +46,7 @@ extension FpdartOnIterable on Iterable { /// at that point, the returned iterable will also be empty, same /// as if this iterable has only one element. Option> get tail { - if (isEmpty) return None(); + if (isEmpty) return const None(); return Some(skip(1)); } @@ -60,7 +60,7 @@ extension FpdartOnIterable on Iterable { /// at that point, the returned iterable will also be empty, same /// as if this iterable has only one element. Option> get init { - if (isEmpty) return None(); + if (isEmpty) return const None(); return Some(this.dropRight(1)); } @@ -331,7 +331,7 @@ extension FpdartOnIterable on Iterable { } return Some(min); } - return None(); + return const None(); } /// The least element of this [Iterable] based on `order`. @@ -348,7 +348,7 @@ extension FpdartOnIterable on Iterable { } 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 on Map { 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 on Map { 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 on Map { Option extract(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 extends IEffect { static Option safeCastStrict(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 extends IEffect { dynamic json, R Function(dynamic json) fromJson, ) => - json != null ? Option.tryCatch(() => fromJson(json)) : None(); + json != null ? Option.tryCatch(() => fromJson(json)) : const None(); static Iterable getSomes(Iterable> iterable) sync* { for (var option in iterable) { @@ -115,21 +115,18 @@ final class Some extends Option { @override Option filter(bool Function(R r) f) { if (f(value)) return Some(value); - return None(); + return const None(); } @override Option filterMap(Option 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 { - static const None _none = None._instance(); - const None._instance(); - - factory None() => _none; + const None(); @override Effect get asEffect => @@ -143,20 +140,20 @@ final class None extends Option { 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 andThen(C Function(Never r) f) => None(); + Option andThen(C Function(Never r) f) => const None(); @override - Option tap(Option Function(Never r) f) => None(); + Option tap(Option Function(Never r) f) => const None(); @override - Option filter(bool Function(Never r) f) => None(); + Option filter(bool Function(Never r) f) => const None(); @override - Option filterMap(Option Function(Never r) f) => None(); + Option filterMap(Option 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 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 = ({ FutureOr Function(IEffect) async, @@ -160,6 +161,10 @@ final class Effect extends IEffect { (_) => f().then(Right.new), ); + /// {@category constructors} + factory Effect.lazy(Effect 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 extends IEffect { Effect race(Effect effect) => Effect.raceAll([this, effect]); + /// {@category sequencing} + Effect alwaysIgnore(Effect effect) => Effect.from( + (context) => race(context.signal.wait()).__unsafeRun(context).then( + (exit) => effect.__unsafeRun(context.withoutSignal).then( + (_) => exit, + ), + ), + ); + /// {@category sequencing} Effect flatMap(Effect Function(R r) f) => Effect.from( (context) => _unsafeRun(context).then( @@ -632,6 +646,20 @@ final class Effect extends IEffect { /// {@category interruption} Effect interrupt() => Effect.failCause(const Interrupted()); + + /// {@category scoping} + Effect, L, R> get scopedEnv => Effect.from( + (context) => __unsafeRun(context.withEnv(context.env.env)), + ); +} + +extension EffectWithScope on Effect, L, R> { + Effect get scoped => Effect.from((context) { + final scope = Scope.withEnv(context.env); + return alwaysIgnore(scope.closeScope()).__unsafeRun( + context.withEnv(scope), + ); + }); } extension ProvideNull on Effect { 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 on Iterable> { Iterable get flatten => expand(identity); } +extension IterableEffect on Iterable> { + Effect> get all => Effect.all(this); +} + extension FpdartSequenceIterableOption on Iterable> { Iterable 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 addScopeFinalizer( + Effect finalizer, + ) => + Effect.functionSucceed(() { + scopeFinalizers.add(finalizer); + return unit; + }); + + Effect removeScopeFinalizer( + Effect finalizer, + ) => + Effect.functionSucceed(() { + scopeFinalizers.remove(finalizer); + return unit; + }); + + Effect closeScope() => Effect.lazy( + () => scopeFinalizers.reversed.all.zipRight(Effect.functionSucceed( + () { + scopeFinalizers.clear(); + return unit; + }, + )).withEnv(), + ); +} + +class Scope 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 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 extends IEffect { ); /// {@category context} - Effect provide(Context context) => - Effect.from((_) => _unsafeRun(context)); + Effect provide(Context 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 provideEnv(E env) => - Effect.from((_) => _unsafeRun(Context.env(env))); + Effect 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 provideEffect(Effect effect) => Effect.from( @@ -504,10 +513,30 @@ final class Effect extends IEffect { ), ); + /// {@category sequencing} + Effect flatMapEnv(Effect 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 tap(Effect Function(R r) f) => flatMap((r) => f(r).map((_) => r)); + /// {@category sequencing} + Effect tapEnv( + Effect Function(R r, E env) f, + ) => + flatMapEnv( + (r, env) => f(r, env).map((_) => r), + ); + /// {@category sequencing} Effect tapError(Effect Function(L l) f) => Effect.from( (context) => _unsafeRun(context).then( @@ -646,15 +675,45 @@ final class Effect extends IEffect { /// {@category interruption} Effect interrupt() => Effect.failCause(const Interrupted()); +} + +extension EffectWithScopeFinalizer + on Effect { + /// {@category scoping} + Effect addFinalizer(Effect release) => + tapEnv((_, env) => env.addScopeFinalizer(release)); + + /// {@category scoping} + Effect acquireRelease( + Effect Function(R r) release, + ) => + tap((r) => addFinalizer(release(r))); +} +extension EffectNoScopeFinalizer on Effect { /// {@category scoping} - Effect, L, R> get scopedEnv => Effect.from( - (context) => __unsafeRun(context.withEnv(context.env.env)), + Effect, L, R> get withScope => Effect, L, R>.from( + (ctx) => _unsafeRun(ctx.withEnv(ctx.env.env)), + ); + + /// {@category scoping} + Effect, L, R> addFinalizer(Effect release) => + withScope.tapEnv( + (_, env) => env.addScopeFinalizer(release), + ); + + /// {@category scoping} + Effect, L, R> acquireRelease( + Effect Function(R r) release, + ) => + withScope.tapEnv( + (r, _) => _.addScopeFinalizer(release(r)), ); } extension EffectWithScope on Effect, L, R> { - Effect get scoped => Effect.from((context) { + /// {@category scoping} + Effect 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.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.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.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 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 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.succeed(10).withScope.addFinalizer( + Effect.functionSucceed(() { + mutable += 1; + return unit; + }), + ); + + await main.provide(Context.env(scope)).runFuture(); + expect(mutable, 0); + scope.closeScope().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 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 From 8e34681c6beae8cc474eabb549a5420c91c356da Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 extends IEffect { return "Effect(${_unsafeRun.runtimeType})"; } - FutureOr> __unsafeRun(Context 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> Function(Context 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 catch (cause) { return Left(cause); } catch (error, stackTrace) { @@ -163,7 +155,7 @@ final class Effect extends IEffect { /// {@category constructors} factory Effect.lazy(Effect 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 extends IEffect { 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 extends IEffect { } } - return deferred.wait().__unsafeRun(context).then( + return deferred.wait()._unsafeRun(context).then( (exit) => signal .failCause(const Interrupted()) - .__unsafeRun(context.withoutSignal) + ._unsafeRun(context.withoutSignal) .then((_) => exit), ); }); @@ -496,8 +488,8 @@ final class Effect extends IEffect { /// {@category sequencing} Effect alwaysIgnore(Effect 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 on Effect, L, R> { /// {@category scoping} Effect 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 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 extends IEffect { factory Effect.tryCatch({ required FutureOr Function() execute, required L Function(Object error, StackTrace stackTrace) onError, - FutureOr Function()? onCancel, }) => Effect.from( (env) { From 2f802400ac49b9a0ec0d5ebdc4024b0fef731d7a Mon Sep 17 00:00:00 2001 From: SandroMaglione 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( - (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 { } Effect completeExit(Exit 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 extends IEffect { ); /// {@category constructors} - factory Effect.functionFail(FutureOr> Function() f) => Effect.from( + factory Effect.failLazy(FutureOr> Function() f) => Effect.from( (_) => f().then(Left.new), ); /// {@category constructors} - factory Effect.functionSucceed(FutureOr Function() f) => Effect.from( + factory Effect.succeedLazy(FutureOr 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 addScopeFinalizer( Effect finalizer, ) => - Effect.functionSucceed(() { + Effect.succeedLazy(() { scopeFinalizers.add(finalizer); return unit; }); @@ -16,13 +16,13 @@ mixin ScopeMixin { Effect removeScopeFinalizer( Effect finalizer, ) => - Effect.functionSucceed(() { + Effect.succeedLazy(() { scopeFinalizers.remove(finalizer); return unit; }); Effect closeScope() => 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([ 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.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.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.functionSucceed(() { + final result = Effect.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.succeed(10).withScope.addFinalizer( - Effect.functionSucceed(() { + Effect.succeedLazy(() { mutable += 1; return unit; }), @@ -26,7 +26,7 @@ void main() { var mutable = 0; final main = Effect.succeed(10).withScope.addFinalizer( - Effect.functionSucceed(() { + Effect.succeedLazy(() { mutable += 1; return unit; }), @@ -43,7 +43,7 @@ void main() { var mutable = 0; final main = Effect.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.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.succeed(10).tap( - (_) => Effect.functionSucceed(() { + (_) => Effect.succeedLazy(() { mutable += 1; }), ); From f691c1ea85071974f657e6415c4821738b685b59 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 extends IEffect { static Effect> all( Iterable> iterable, ) => - Effect.forEach(iterable, (a, _) => a); + Effect.forEach( + iterable, + (effect, _) => effect, + ); /// {@category zipping} Effect zipWith( @@ -283,22 +286,33 @@ final class Effect extends IEffect { ), ); + Effect _provideEnvCloseScope(E env) => + env is ScopeMixin && !env.scopeClosable + ? Effect.from( + (context) => _unsafeRun(context).then( + (exit) => switch (exit) { + Left(value: final value) => Left(value), + Right(value: final value) => + env.closeScope()._unsafeRun(context).then( + (exit) => switch (exit) { + Left(value: final value) => Left(value), + Right() => Right(value), + }, + ), + }, + ), + ) + : this; + /// {@category context} - Effect provide(Context context) { - final env = context.env; - final effect = env is ScopeMixin && !env.scopeClosable - ? alwaysIgnore(env.closeScope()) - : this; - return Effect.from((_) => effect._unsafeRun(context)); - } + Effect provide(Context context) => Effect.from( + (_) => _provideEnvCloseScope(context.env)._unsafeRun(context), + ); /// {@category context} - Effect provideEnv(E env) { - final effect = env is ScopeMixin && !env.scopeClosable - ? alwaysIgnore(env.closeScope()) - : this; - return Effect.from((_) => effect._unsafeRun(Context.env(env))); - } + Effect provideEnv(E env) => Effect.from( + (_) => _provideEnvCloseScope(env)._unsafeRun(Context.env(env)), + ); /// {@category context} Effect provideEffect(Effect effect) => Effect.from( @@ -682,14 +696,11 @@ extension EffectWithScopeFinalizer } extension EffectNoScopeFinalizer on Effect { - /// {@category scoping} - Effect, L, R> get withScope => Effect, L, R>.from( - (ctx) => _unsafeRun(ctx.withEnv(ctx.env.env)), - ); - /// {@category scoping} Effect, L, R> addFinalizer(Effect release) => - withScope.tapEnv( + Effect, L, R>.from( + (context) => _unsafeRun(context.withEnv(context.env.env)), + ).tapEnv( (_, env) => env.addScopeFinalizer(release), ); @@ -697,18 +708,20 @@ extension EffectNoScopeFinalizer on Effect { Effect, L, R> acquireRelease( Effect Function(R r) release, ) => - withScope.tapEnv( - (r, _) => _.addScopeFinalizer(release(r)), + Effect, L, R>.from( + (context) => _unsafeRun(context.withEnv(context.env.env)), + ).tapEnv( + (r, env) => env.addScopeFinalizer( + release(r), + ), ); } extension EffectWithScope on Effect, L, R> { - /// {@category scoping} + /// {@category context} Effect 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.succeed(10) + .addFinalizer(Effect.succeedLazy( + () => throw const CustomError(), + )) + .provideEnv(Scope.withEnv(null)); + + final result = await main.runFutureExit(); + + result.expectLeft((value) { + expect(value, isA()); + if (value is Die) { + expect(value.error, isA()); + } + }); + }); + + test('handle failure in closing scope', () async { + final main = Effect.succeed(10) + .addFinalizer(Effect.die(const CustomError())) + .provideEnv(Scope.withEnv(null)); + + final result = await main.runFutureExit(); + + result.expectLeft((value) { + expect(value, isA()); + if (value is Die) { + expect(value.error, isA()); + } + }); + }); + }); + + group('provideScope', () { + test('handle throw in closing scope', () async { + final main = Effect.succeed(10) + .addFinalizer(Effect.succeedLazy( + () => throw const CustomError(), + )) + .provideScope; + + final result = await main.runFutureExit(); + + result.expectLeft((value) { + expect(value, isA()); + if (value is Die) { + expect(value.error, isA()); + } + }); + }); + + test('handle failure in closing scope', () async { + final main = Effect.succeed(10) + .addFinalizer(Effect.die(const CustomError())) + .provideScope; + + final result = await main.runFutureExit(); + + result.expectLeft((value) { + expect(value, isA()); + if (value is Die) { + expect(value.error, isA()); + } + }); + }); + }); + }, + ); +} 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()); - } + + result.expectLeft((value) { + expect(value, isA()); + }); }); }); }, 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 on Either { + 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 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.succeed(10).withScope.addFinalizer( + final main = Effect.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.succeed(10).withScope.addFinalizer( + final main = Effect.succeed(10).addFinalizer( Effect.succeedLazy(() { mutable += 1; return unit; From 1118bd66039da1ac926b178ab347be92117b5df8 Mon Sep 17 00:00:00 2001 From: SandroMaglione 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 { + final _deferred = Deferred.unsafeMake(); + + void succeed(R value) => _deferred.unsafeCompleteExit(Right(value)); + void fail(L error) => _deferred.unsafeCompleteExit(Left(Failure(error))); + void failCause(Cause 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 extends IEffect { (_) => f().then(Right.new), ); + /// {@category constructors} + factory Effect.fail(L value) => Effect.from((_) => Left(Failure(value))); + + /// {@category constructors} + factory Effect.failCause(Cause cause) => Effect.from((_) => Left(cause)); + + /// {@category constructors} + factory Effect.succeed(R value) => Effect.from((_) => Right(value)); + /// {@category constructors} factory Effect.lazy(Effect 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 resume) callback) => + Effect.from( + (context) { + final asyncContext = AsyncContext(); + callback(asyncContext); + return asyncContext._deferred.wait()._unsafeRun(context); + }, + ); /// {@category constructors} - factory Effect.failCause(Cause cause) => Effect.from((_) => Left(cause)); + factory Effect.asyncInterrupt( + Effect Function(AsyncContext resume) callback, + ) => + Effect.from((context) { + final asyncContext = AsyncContext(); + + final finalizer = callback(asyncContext); + if (asyncContext._deferred.unsafeCompleted) { + return asyncContext._deferred.wait()._unsafeRun(context); + } + + final interruption = context.signal.wait().alwaysIgnore( + finalizer.withEnv(), + ); + + return asyncContext._deferred + .wait() + .race(interruption) + ._unsafeRun(context.withoutSignal); + }); /// {@category constructors} - factory Effect.succeed(R value) => Effect.from((_) => Right(value)); + static Effect sleep(Duration duration) => + Effect.asyncInterrupt( + (resume) { + final timer = Timer(duration, () { + resume.succeed(null); + }); + + if (resume._deferred.unsafeCompleted) { + timer.cancel(); + return resume._deferred.wait().match( + onFailure: (_) => fpdart_unit.unit, + onSuccess: (_) => fpdart_unit.unit, + ); + } + + return Effect.unit(); + }, + ); /// {@category constructors} factory Effect.raceAll(Iterable> iterable) => @@ -189,14 +242,6 @@ final class Effect extends IEffect { ); }); - /// {@category constructors} - static Effect sleep(Duration duration) => Effect.from( - (_) => Future.delayed( - duration, - () => const Right(null), - ), - ); - /// {@category constructors} static Effect die(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.async( + (resume) => resume.succeed(10), + ); + final result = await main.runFuture(); + expect(result, 10); + }); + + test('fail callback', () async { + final main = Effect.async( + (resume) => resume.fail("error"), + ); + final result = await main.flip().runFuture(); + expect(result, "error"); + }); + + test('succeed async callback', () async { + final main = Effect.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.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 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 on Effect { ); /// {@category execution} - R runSync() { + R runSyncOrThrow() { try { final result = _unsafeRun(Context.env(null)); if (result is Future) { @@ -815,7 +815,7 @@ extension ProvideNull on Effect { } /// {@category execution} - Future runFuture() async { + Future 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.succeed(10).orDie; - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, 10); }); test('fail', () { final main = Effect.fail("error").orDie; - expect(() => main.runSync(), throwsA(isA())); + expect(() => main.runSyncOrThrow(), throwsA(isA())); }); }); @@ -24,14 +24,14 @@ void main() { test('succeed', () { final main = Effect.succeed(10) .orDieWith((_) => CustomError()); - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, 10); }); test('fail', () { final main = Effect.fail("error") .orDieWith((_) => CustomError()); - expect(() => main.runSync(), throwsA(isA())); + expect(() => main.runSyncOrThrow(), throwsA(isA())); }); }); }, 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.succeed(10); - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, 10); }); test('fail', () { final main = Effect.fail("error"); - final result = main.flip().runSync(); + final result = main.flip().runSyncOrThrow(); expect(result, "error"); }); @@ -22,7 +22,7 @@ void main() { final main = Effect.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.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())); + expect(() => main.runSyncOrThrow(), throwsA(isA())); }); }); }, 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.succeed(10)); - final result = program.runSync(); + final result = program.runSyncOrThrow(); expect(result, 11); }); @@ -39,7 +39,7 @@ void main() { final program = main.provideEffect(Effect.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().runSync(); + scope.closeScope().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.succeed(10).zipLeft(Effect.succeed("10")); - final result = main.runSync(); + final result = main.runSyncOrThrow(); expect(result, 10); }); test('zipRight', () { final main = Effect.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(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(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 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>.gen(($) async { + final env = $.sync(Effect.env()); + final inputFile = File(env.wordPairCsv); + + final lines = inputFile.openRead().transform(utf8.decoder).transform( + LineSplitter(), + ); + + final wordCollection = []; + 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>.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 = {}; + 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 = []; + 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.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 get props => [idWord]; + + Map 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 get props => [word1, word2]; + + Map 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 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 extends IEffect { resume.succeed(null); }); - if (resume._deferred.unsafeCompleted) { + return Effect.succeedLazy(() { timer.cancel(); - return resume._deferred.wait().match( - onFailure: (_) => fpdart_unit.unit, - onSuccess: (_) => fpdart_unit.unit, - ); - } - - return Effect.unit(); + return fpdart_unit.unit; + }); }, );