From bed477ad8660cd670e9ddd534f522eb4b6af17c5 Mon Sep 17 00:00:00 2001 From: SandroMaglione Date: Sat, 30 Mar 2024 06:22:30 +0900 Subject: [PATCH] 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; +}