polysemy-conc-0.10.0.0: lib/Polysemy/Conc/Interpreter/Scoped.hs
{-# options_haddock prune #-}
-- |Description: Scoped Interpreters, Internal
module Polysemy.Conc.Interpreter.Scoped where
import GHC.Err (errorWithoutStackTrace)
import Polysemy.Internal (Sem (Sem), hoistSem, liftSem, runSem)
import Polysemy.Internal.Sing (KnownList (singList))
import Polysemy.Internal.Tactics (liftT, runTactics)
import Polysemy.Internal.Union (
Union (Union),
Weaving (Weaving),
decomp,
extendMembershipLeft,
hoist,
injWeaving,
injectMembership,
)
import Polysemy.Membership (ElemOf)
import Polysemy.Resume (Stop, runStop, type (!!))
import Polysemy.Resume.Effect.Resumable (Resumable (Resumable))
import Polysemy.Conc.Effect.Scoped (Scoped (InScope, Run))
interpretWeaving ::
∀ e r .
(∀ x . Weaving e (Sem (e : r)) x -> Sem r x) ->
InterpreterFor e r
interpretWeaving h (Sem m) =
Sem \ k -> m $ decomp >>> \case
Right wav -> runSem (h wav) k
Left g -> k (hoist (interpretWeaving h) g)
restack :: (∀ e . ElemOf e r -> ElemOf e r')
-> Sem r a
-> Sem r' a
restack n =
hoistSem $ \ (Union pr wav) -> hoist (restack n) (Union (n pr) wav)
{-# inline restack #-}
-- | Construct an interpreter for a higher-order effect wrapped in a 'Scoped',
-- given a resource allocation function and a parameterized handler for the
-- plain effect.
--
-- This combinator is analogous to 'interpretH' in that it allows the handler to
-- use the 'Tactical' environment and transforms the effect into other effects
-- on the stack.
interpretScopedH ::
∀ resource param effect r .
-- | A callback function that allows the user to acquire a resource for each
-- computation wrapped by 'Polysemy.Conc.scoped' using other effects, with an additional
-- argument that contains the call site parameter passed to 'Polysemy.Conc.scoped'.
(∀ x . param -> (resource -> Sem r x) -> Sem r x) ->
-- | A handler like the one expected by 'interpretH' with an additional
-- parameter that contains the @resource@ allocated by the first argument.
(∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) r x) ->
InterpreterFor (Scoped param effect) r
interpretScopedH withResource scopedHandler =
go (errorWithoutStackTrace "top level run")
where
go :: resource -> InterpreterFor (Scoped param effect) r
go resource =
interpretWeaving \ (Weaving effect s wv ex ins) -> case effect of
Run act ->
ex <$> runTactics s (raise . go resource . wv) ins (go resource . wv)
(scopedHandler resource act)
InScope param main ->
withResource param \ resource' -> ex <$> go resource' (wv (main <$ s))
{-# inline interpretScopedH #-}
-- | Variant of 'interpretScopedH' that allows the resource acquisition function
-- to use 'Tactical'.
interpretScopedH' ::
∀ resource param effect r .
(∀ e r0 x . param -> (resource -> Tactical e (Sem r0) r x) ->
Tactical e (Sem r0) r x) ->
(∀ r0 x .
resource -> effect (Sem r0) x ->
Tactical (Scoped param effect) (Sem r0) r x) ->
InterpreterFor (Scoped param effect) r
interpretScopedH' withResource scopedHandler =
go (errorWithoutStackTrace "top level run")
where
go :: resource -> InterpreterFor (Scoped param effect) r
go resource =
interpretH \case
Run act ->
scopedHandler resource act
InScope param main ->
withResource param \ resource' ->
raise . go resource' =<< runT main
{-# inline interpretScopedH' #-}
-- | First-order variant of 'interpretScopedH'.
interpretScoped ::
∀ resource param effect r .
(∀ x . param -> (resource -> Sem r x) -> Sem r x) ->
(∀ m x . resource -> effect m x -> Sem r x) ->
InterpreterFor (Scoped param effect) r
interpretScoped withResource scopedHandler =
interpretScopedH withResource \ r e -> liftT (scopedHandler r e)
{-# inline interpretScoped #-}
-- | Variant of 'interpretScoped' in which the resource allocator is a plain
-- action.
interpretScopedAs ::
∀ resource param effect r .
(param -> Sem r resource) ->
(∀ m x . resource -> effect m x -> Sem r x) ->
InterpreterFor (Scoped param effect) r
interpretScopedAs resource =
interpretScoped \ p use -> use =<< resource p
{-# inline interpretScopedAs #-}
-- | Higher-order interpreter for 'Scoped' that allows the handler to use
-- additional effects that are interpreted by the resource allocator.
--
-- /Note/: It is necessary to specify the list of local interpreters with a type
-- application; GHC won't be able to figure them out from the type of
-- @withResource@.
--
-- As an example for a higher order effect, consider a mutexed concurrent state
-- effect, where an effectful function may lock write access to the state while
-- making it still possible to read it:
--
-- > data MState s :: Effect where
-- > MState :: (s -> m (s, a)) -> MState s m a
-- > MRead :: MState s m s
-- >
-- > makeSem ''MState
--
-- We can now use an 'Polysemy.AtomicState.AtomicState' to store the current
-- value and lock write access with an @MVar@. Since the state callback is
-- effectful, we need a higher order interpreter:
--
-- > withResource ::
-- > Member (Embed IO) r =>
-- > s ->
-- > (MVar () -> Sem (AtomicState s : r) a) ->
-- > Sem r a
-- > withResource initial use = do
-- > tv <- embed (newTVarIO initial)
-- > lock <- embed (newMVar ())
-- > runAtomicStateTVar tv $ use lock
-- >
-- > interpretMState ::
-- > ∀ s r .
-- > Members [Resource, Embed IO] r =>
-- > InterpreterFor (Scoped s (MState s)) r
-- > interpretMState =
-- > interpretScopedWithH @'[AtomicState s] withResource \ lock -> \case
-- > MState f ->
-- > bracket_ (embed (takeMVar lock)) (embed (tryPutMVar lock ())) do
-- > s0 <- atomicGet
-- > res <- runTSimple (f s0)
-- > Inspector ins <- getInspectorT
-- > for_ (ins res) \ (s, _) -> atomicPut s
-- > pure (snd <$> res)
-- > MRead ->
-- > liftT atomicGet
interpretScopedWithH ::
∀ extra resource param effect r r1 .
r1 ~ (extra ++ r) =>
KnownList extra =>
(∀ x . param -> (resource -> Sem r1 x) -> Sem r x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) r1 x) ->
InterpreterFor (Scoped param effect) r
interpretScopedWithH withResource scopedHandler =
interpretWeaving \case
Weaving (InScope param main) s wv ex _ ->
ex <$> withResource param \ resource -> inScope resource $
restack
(injectMembership
(singList @'[Scoped param effect])
(singList @extra)) $ wv (main <$ s)
_ ->
errorWithoutStackTrace "top level Run"
where
inScope :: resource -> InterpreterFor (Scoped param effect) r1
inScope resource =
interpretWeaving \case
Weaving (InScope param main) s wv ex _ ->
restack (extendMembershipLeft (singList @extra))
(ex <$> withResource param \resource' ->
inScope resource' (wv (main <$ s)))
Weaving (Run act) s wv ex ins ->
ex <$> runTactics s (raise . inScope resource . wv) ins (inScope resource . wv)
(scopedHandler resource act)
{-# inline interpretScopedWithH #-}
-- | First-order variant of 'interpretScopedWithH'.
--
-- /Note/: It is necessary to specify the list of local interpreters with a type
-- application; GHC won't be able to figure them out from the type of
-- @withResource@:
--
-- > data SomeAction :: Effect where
-- > SomeAction :: SomeAction m ()
-- >
-- > foo :: InterpreterFor (Scoped () SomeAction) r
-- > foo =
-- > interpretScopedWith @[Reader Int, State Bool] localEffects \ () -> \case
-- > SomeAction -> put . (> 0) =<< ask @Int
-- > where
-- > localEffects () use = evalState False (runReader 5 (use ()))
interpretScopedWith ::
∀ extra param resource effect r r1 .
r1 ~ (extra ++ r) =>
KnownList extra =>
(∀ x . param -> (resource -> Sem r1 x) -> Sem r x) ->
(∀ m x . resource -> effect m x -> Sem r1 x) ->
InterpreterFor (Scoped param effect) r
interpretScopedWith withResource scopedHandler =
interpretScopedWithH @extra withResource \ r e -> liftT (scopedHandler r e)
{-# inline interpretScopedWith #-}
-- | Variant of 'interpretScopedWith' in which no resource is used and the
-- resource allocator is a plain interpreter.
-- This is useful for scopes that only need local effects, but no resources in
-- the handler.
--
-- See the /Note/ on 'interpretScopedWithH'.
interpretScopedWith_ ::
∀ extra param effect r r1 .
r1 ~ (extra ++ r) =>
KnownList extra =>
(∀ x . param -> Sem r1 x -> Sem r x) ->
(∀ m x . effect m x -> Sem r1 x) ->
InterpreterFor (Scoped param effect) r
interpretScopedWith_ withResource scopedHandler =
interpretScopedWithH @extra (\ p f -> withResource p (f ())) \ () e -> liftT (scopedHandler e)
{-# inline interpretScopedWith_ #-}
-- | Variant of 'interpretScoped' that uses another interpreter instead of a
-- handler.
--
-- This is mostly useful if you want to reuse an interpreter that you cannot
-- easily rewrite (like from another library). If you have full control over the
-- implementation, 'interpretScoped' should be preferred.
--
-- /Note/: The wrapped interpreter will be executed fully, including the
-- initializing code surrounding its handler, for each action in the program, so
-- if the interpreter allocates any resources, they will be scoped to a single
-- action. Move them to @withResource@ instead.
--
-- For example, consider the following interpreter for
-- 'Polysemy.AtomicState.AtomicState':
--
-- > atomicTVar :: Member (Embed IO) r => a -> InterpreterFor (AtomicState a) r
-- > atomicTVar initial sem = do
-- > tv <- embed (newTVarIO initial)
-- > runAtomicStateTVar tv sem
--
-- If this interpreter were used for a scoped version of @AtomicState@ like
-- this:
--
-- > runScoped (\ initial use -> use initial) \ initial -> atomicTVar initial
--
-- Then the @TVar@ would be created every time an @AtomicState@ action is run,
-- not just when entering the scope.
--
-- The proper way to implement this would be to rewrite the resource allocation:
--
-- > runScoped (\ initial use -> use =<< embed (newTVarIO initial)) runAtomicStateTVar
runScoped ::
∀ resource param effect r .
(∀ x . param -> (resource -> Sem r x) -> Sem r x) ->
(resource -> InterpreterFor effect r) ->
InterpreterFor (Scoped param effect) r
runScoped withResource scopedInterpreter =
go (errorWithoutStackTrace "top level run")
where
go :: resource -> InterpreterFor (Scoped param effect) r
go resource =
interpretWeaving \ (Weaving effect s wv ex ins) -> case effect of
Run act ->
scopedInterpreter resource
$ liftSem $ injWeaving $ Weaving act s (raise . go resource . wv) ex ins
InScope param main ->
withResource param \ resource' -> ex <$> go resource' (wv (main <$ s))
{-# inline runScoped #-}
-- | Variant of 'runScoped' in which the resource allocator returns the resource
-- rather tnen calling a continuation.
runScopedAs ::
∀ resource param effect r .
(param -> Sem r resource) ->
(resource -> InterpreterFor effect r) ->
InterpreterFor (Scoped param effect) r
runScopedAs resource = runScoped \ p use -> use =<< resource p
{-# inline runScopedAs #-}
-- |Combined higher-order interpreter for 'Scoped' and 'Resumable'.
-- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the
-- entire scope.
interpretScopedResumableH ::
∀ param resource effect err r .
(∀ x . param -> (resource -> Sem (Stop err : r) x) -> Sem (Stop err : r) x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) (Stop err : r) x) ->
InterpreterFor (Scoped param effect !! err) r
interpretScopedResumableH withResource scopedHandler =
run (errorWithoutStackTrace "top level run")
where
run :: resource -> InterpreterFor (Scoped param effect !! err) r
run resource =
interpretWeaving \ (Weaving (Resumable inner) s' dist' ex' ins') ->
case inner of
Weaving effect s dist ex ins -> do
let
handleScoped = \case
Run act ->
scopedHandler resource act
InScope param main ->
raise (withResource param \ resource' -> Compose <$> raise (run resource' (dist' (dist (main <$ s) <$ s'))))
tac =
runTactics
(Compose (s <$ s'))
(raise . raise . run resource . fmap Compose . dist' . fmap dist . getCompose)
(ins <=< ins' . getCompose)
(raise . run resource . fmap Compose . dist' . fmap dist . getCompose)
(handleScoped effect)
exFinal = ex' . \case
Right (Compose a) -> Right . ex <$> a
Left err -> Left err <$ s'
exFinal <$> runStop tac
-- |Combined interpreter for 'Scoped' and 'Resumable'.
-- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the
-- entire scope.
interpretScopedResumable ::
∀ param resource effect err r .
(∀ x . param -> (resource -> Sem (Stop err : r) x) -> Sem (Stop err : r) x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop err : r) x) ->
InterpreterFor (Scoped param effect !! err) r
interpretScopedResumable withResource scopedHandler =
interpretScopedResumableH withResource \ r e -> liftT (scopedHandler r e)
-- |Combined interpreter for 'Scoped' and 'Resumable'.
-- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the
-- entire scope.
-- In this variant, the resource allocator is a plain action.
interpretScopedResumable_ ::
∀ param resource effect err r .
(param -> Sem (Stop err : r) resource) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop err : r) x) ->
InterpreterFor (Scoped param effect !! err) r
interpretScopedResumable_ resource =
interpretScopedResumable \ p use -> use =<< resource p
-- |Combined higher-order interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects
-- that are interpreted by the resource allocator.
-- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the
-- entire scope.
interpretScopedResumableWithH ::
∀ extra param resource effect err r r1 .
r1 ~ (extra ++ (Stop err : r)) =>
r1 ~ ((extra ++ '[Stop err]) ++ r) =>
KnownList extra =>
KnownList (extra ++ '[Stop err]) =>
(∀ x . param -> (resource -> Sem r1 x) -> Sem (Stop err : r) x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) r1 x) ->
InterpreterFor (Scoped param effect !! err) r
interpretScopedResumableWithH withResource scopedHandler =
run (errorWithoutStackTrace "top level run")
where
run :: resource -> InterpreterFor (Scoped param effect !! err) r
run resource =
interpretWeaving \ (Weaving (Resumable inner) s' dist' ex' ins') ->
case inner of
Weaving effect s dist ex ins -> do
let
handleScoped = \case
Run _ ->
error "top level Run"
InScope param main ->
raise $ withResource param \ resource' ->
Compose <$> inScope resource' (restack (injectMembership (singList @'[Scoped param effect !! err]) (singList @(extra ++ '[Stop err]))) (dist' (dist (main <$ s) <$ s')))
tac =
runTactics
(Compose (s <$ s'))
(raise . raise . run resource . fmap Compose . dist' . fmap dist . getCompose)
(ins <=< ins' . getCompose)
(raise . run resource . fmap Compose . dist' . fmap dist . getCompose)
(handleScoped effect)
exFinal = ex' . \case
Right (Compose a) -> Right . ex <$> a
Left err -> Left err <$ s'
exFinal <$> runStop tac
inScope :: resource -> InterpreterFor (Scoped param effect !! err) r1
inScope resource =
interpretWeaving \ (Weaving (Resumable inner) s' dist' ex' ins') ->
case inner of
Weaving effect s dist ex ins -> do
let
handleScoped = \case
Run act ->
scopedHandler resource act
InScope param main ->
raise $ restack (extendMembershipLeft (singList @extra)) $ withResource param \ resource' ->
Compose <$> inScope resource' (dist' (dist (main <$ s) <$ s'))
tac =
runTactics
(Compose (s <$ s'))
(raise . inScope resource . fmap Compose . dist' . fmap dist . getCompose)
(ins <=< ins' . getCompose)
(inScope resource . fmap Compose . dist' . fmap dist . getCompose)
(handleScoped effect)
exFinal = ex' . \case
Right (Compose a) -> Right . ex <$> a
Left err -> Left err <$ s'
exFinal <$> runStop (raise tac)
-- |Combined interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects that are
-- interpreted by the resource allocator.
-- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the
-- entire scope.
interpretScopedResumableWith ::
∀ extra param resource effect err r r1 .
r1 ~ (extra ++ (Stop err : r)) =>
r1 ~ ((extra ++ '[Stop err]) ++ r) =>
KnownList extra =>
KnownList (extra ++ '[Stop err]) =>
(∀ x . param -> (resource -> Sem r1 x) -> Sem (Stop err : r) x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Sem r1 x) ->
InterpreterFor (Scoped param effect !! err) r
interpretScopedResumableWith withResource scopedHandler =
interpretScopedResumableWithH @extra withResource \ r e -> liftT (scopedHandler r e)
-- |Combined interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects that are
-- interpreted by the resource allocator.
-- This allows 'Stop' to be sent from within the resource allocator so that the consumer receives it, terminating the
-- entire scope.
-- In this variant, no resource is used and the allocator is a plain interpreter.
interpretScopedResumableWith_ ::
∀ extra param effect err r r1 .
r1 ~ (extra ++ (Stop err : r)) =>
r1 ~ ((extra ++ '[Stop err]) ++ r) =>
KnownList extra =>
KnownList (extra ++ '[Stop err]) =>
(∀ x . param -> Sem r1 x -> Sem (Stop err : r) x) ->
(∀ r0 x . effect (Sem r0) x -> Sem r1 x) ->
InterpreterFor (Scoped param effect !! err) r
interpretScopedResumableWith_ extra scopedHandler =
interpretScopedResumableWith @extra (\ p f -> extra p (f ())) (const scopedHandler)
-- |Combined higher-order interpreter for 'Resumable' and 'Scoped'.
-- In this variant, only the handler may send 'Stop', but this allows resumption to happen on each action inside of the
-- scope.
interpretResumableScopedH ::
∀ param resource effect err r .
(∀ x . param -> (resource -> Sem r x) -> Sem r x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) (Stop err : r) x) ->
InterpreterFor (Scoped param (effect !! err)) r
interpretResumableScopedH withResource scopedHandler =
run (errorWithoutStackTrace "top level run")
where
run :: resource -> InterpreterFor (Scoped param (effect !! err)) r
run resource =
interpretWeaving \ (Weaving inner s' dist' ex' ins') -> case inner of
Run (Resumable (Weaving effect s dist ex ins)) ->
exFinal <$> runStop (tac (scopedHandler resource effect))
where
tac =
runTactics
(Compose (s <$ s'))
(raise . raise . run resource . fmap Compose . dist' . fmap dist . getCompose)
(ins <=< ins' . getCompose)
(raise . run resource . fmap Compose . dist' . fmap dist . getCompose)
exFinal = ex' . \case
Right (Compose a) -> Right . ex <$> a
Left err -> Left err <$ s'
InScope param main ->
ex' <$> withResource param \ resource' -> run resource' (dist' (main <$ s'))
-- |Combined interpreter for 'Resumable' and 'Scoped'.
-- In this variant, only the handler may send 'Stop', but this allows resumption to happen on each action inside of the
-- scope.
interpretResumableScoped ::
∀ param resource effect err r .
(∀ x . param -> (resource -> Sem r x) -> Sem r x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop err : r) x) ->
InterpreterFor (Scoped param (effect !! err)) r
interpretResumableScoped withResource scopedHandler =
interpretResumableScopedH withResource \ r e -> liftT (scopedHandler r e)
-- |Combined interpreter for 'Resumable' and 'Scoped'.
-- In this variant:
-- - Only the handler may send 'Stop', but this allows resumption to happen on each action inside of the scope.
-- - The resource allocator is a plain action.
interpretResumableScoped_ ::
∀ param resource effect err r .
(param -> Sem r resource) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop err : r) x) ->
InterpreterFor (Scoped param (effect !! err)) r
interpretResumableScoped_ resource =
interpretResumableScoped \ p use -> use =<< resource p
-- |Combined higher-order interpreter for 'Resumable' and 'Scoped' that allows the handler to use additional effects
-- that are interpreted by the resource allocator.
-- In this variant, only the handler may send 'Stop', but this allows resumption to happen on each action inside of the
-- scope.
interpretResumableScopedWithH ::
∀ extra param resource effect err r r1 .
r1 ~ (extra ++ r) =>
KnownList extra =>
(∀ x . param -> (resource -> Sem r1 x) -> Sem r x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) (Stop err : r1) x) ->
InterpreterFor (Scoped param (effect !! err)) r
interpretResumableScopedWithH withResource scopedHandler =
interpretWeaving \case
Weaving (InScope param main) s dist ex _ ->
ex <$> withResource param \ resource' -> inScope resource' (restack (injectMembership (singList @'[Scoped param (effect !! err)]) (singList @extra)) (dist (main <$ s)))
_ ->
errorWithoutStackTrace "top level run"
where
inScope :: resource -> InterpreterFor (Scoped param (effect !! err)) r1
inScope resource =
interpretWeaving \case
Weaving (Run (Resumable (Weaving effect s dist ex ins))) s' dist' ex' ins' ->
exFinal <$> runStop (tac (scopedHandler resource effect))
where
tac =
runTactics
(Compose (s <$ s'))
(raise . raise . inScope resource . fmap Compose . dist' . fmap dist . getCompose)
(ins <=< ins' . getCompose)
(raise . inScope resource . fmap Compose . dist' . fmap dist . getCompose)
exFinal = ex' . \case
Right (Compose a) -> Right . ex <$> a
Left err -> Left err <$ s'
Weaving (InScope param main) s wv ex _ ->
restack (extendMembershipLeft (singList @extra)) (ex <$> withResource param \ resource' -> inScope resource' (wv (main <$ s)))
-- |Combined interpreter for 'Resumable' and 'Scoped' that allows the handler to use additional effects that are
-- interpreted by the resource allocator.
-- In this variant, only the handler may send 'Stop', but this allows resumption to happen on each action inside of the
-- scope.
interpretResumableScopedWith ::
∀ extra param resource effect err r r1 .
r1 ~ (extra ++ r) =>
KnownList extra =>
(∀ x . param -> (resource -> Sem r1 x) -> Sem r x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop err : r1) x) ->
InterpreterFor (Scoped param (effect !! err)) r
interpretResumableScopedWith withResource scopedHandler =
interpretResumableScopedWithH @extra withResource \ r e -> liftT (scopedHandler r e)
-- |Combined interpreter for 'Resumable' and 'Scoped' that allows the handler to use additional effects that are
-- interpreted by the resource allocator.
-- In this variant:
-- - Only the handler may send 'Stop', but this allows resumption to happen on each action inside of the scope.
-- - No resource is used and the allocator is a plain interpreter.
interpretResumableScopedWith_ ::
∀ extra param effect err r r1 .
r1 ~ (extra ++ r) =>
KnownList extra =>
(∀ x . param -> Sem r1 x -> Sem r x) ->
(∀ r0 x . effect (Sem r0) x -> Sem (Stop err : r1) x) ->
InterpreterFor (Scoped param (effect !! err)) r
interpretResumableScopedWith_ extra scopedHandler =
interpretResumableScopedWith @extra @param @() @effect @err @r @r1 (\ p f -> extra p (f ())) (const scopedHandler)
-- |Combined higher-order interpreter for 'Resumable' and 'Scoped'.
-- In this variant, both the handler and the scope may send different errors via 'Stop', encoding the concept that the
-- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,
-- continuing the scope execution on resumption.
interpretScopedRH ::
∀ param resource effect eo ei r .
(∀ x . param -> (resource -> Sem (Stop eo : r) x) -> Sem (Stop eo : r) x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) (Stop ei : r) x) ->
InterpreterFor (Scoped param (effect !! ei) !! eo) r
interpretScopedRH withResource scopedHandler =
run (errorWithoutStackTrace "top level run")
where
run :: resource -> InterpreterFor (Scoped param (effect !! ei) !! eo) r
run resource =
interpretWeaving \ (Weaving (Resumable (Weaving inner s' dist' ex' ins')) s'' dist'' ex'' ins'') -> case inner of
Run (Resumable (Weaving effect s dist ex ins)) ->
exFinal <$> runStop @ei (tac (scopedHandler resource effect))
where
tac =
runTactics
(Compose (Compose (s <$ s') <$ s''))
(raise . raise . run resource . fmap Compose . fmap (fmap Compose) . dist'' . fmap dist' . fmap (fmap dist . getCompose) . getCompose)
(ins <=< ins' . getCompose <=< ins'' . getCompose)
(raise . run resource . fmap Compose . fmap (fmap Compose) . dist'' . fmap dist' . fmap (fmap dist . getCompose) . getCompose)
exFinal = \case
Right (Compose fffa) ->
ex'' $ fffa <&> Right . \ (Compose ffa) ->
ex' (Right . ex <$> ffa)
Left err ->
ex'' (Right (ex' (Left err <$ s')) <$ s'')
InScope param main -> do
let
inScope =
raise (withResource param \ resource' -> Compose <$> raise (run resource' (dist'' (dist' (main <$ s') <$ s''))))
tac =
runTactics
(Compose (s' <$ s''))
(raise . raise . run resource . fmap Compose . dist'' . fmap dist' . getCompose)
(ins' <=< ins'' . getCompose)
(raise . run resource . fmap Compose . dist'' . fmap dist' . getCompose)
inScope
exFinal = ex'' . \case
Right (Compose a) -> Right . ex' <$> a
Left err -> Left err <$ s''
exFinal <$> runStop tac
-- |Combined interpreter for 'Scoped' and 'Resumable'.
-- In this variant, both the handler and the scope may send different errors via 'Stop', encoding the concept that the
-- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,
-- continuing the scope execution on resumption.
interpretScopedR ::
∀ param resource effect eo ei r .
(∀ x . param -> (resource -> Sem (Stop eo : r) x) -> Sem (Stop eo : r) x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop ei : r) x) ->
InterpreterFor (Scoped param (effect !! ei) !! eo) r
interpretScopedR withResource scopedHandler =
interpretScopedRH withResource \ r e -> liftT (scopedHandler r e)
-- |Combined interpreter for 'Scoped' and 'Resumable'.
-- In this variant:
-- - Both the handler and the scope may send different errors via 'Stop', encoding the concept that the
-- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,
-- continuing the scope execution on resumption.
-- - The resource allocator is a plain action.
interpretScopedR_ ::
∀ param resource effect eo ei r .
(param -> Sem (Stop eo : r) resource) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop ei : r) x) ->
InterpreterFor (Scoped param (effect !! ei) !! eo) r
interpretScopedR_ resource =
interpretScopedR \ p use -> use =<< resource p
-- |Combined higher-order interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects
-- that are interpreted by the resource allocator.
-- In this variant, both the handler and the scope may send different errors via 'Stop', encoding the concept that the
-- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,
-- continuing the scope execution on resumption.
interpretScopedRWithH ::
∀ extra param resource effect eo ei r r1 .
r1 ~ (extra ++ Stop eo : r) =>
r1 ~ ((extra ++ '[Stop eo]) ++ r) =>
KnownList extra =>
KnownList (extra ++ '[Stop eo]) =>
(∀ x . param -> (resource -> Sem r1 x) -> Sem (Stop eo : r) x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) (Stop ei : r1) x) ->
InterpreterFor (Scoped param (effect !! ei) !! eo) r
interpretScopedRWithH withResource scopedHandler =
run
where
run :: InterpreterFor (Scoped param (effect !! ei) !! eo) r
run =
interpretWeaving \ (Weaving (Resumable (Weaving inner s' dist' ex' ins')) s'' dist'' ex'' ins'') -> case inner of
InScope param main -> do
let
ma =
raise $ withResource param \ resource ->
Compose <$> inScope resource (restack (injectMembership (singList @'[Scoped param (effect !! ei) !! eo]) (singList @(extra ++ '[Stop eo]))) (dist'' (dist' (main <$ s') <$ s'')))
tac =
runTactics
(Compose (s' <$ s''))
(raise . raise . run . fmap Compose . dist'' . fmap dist' . getCompose)
(ins' <=< ins'' . getCompose)
(raise . run . fmap Compose . dist'' . fmap dist' . getCompose)
ma
exFinal = ex'' . \case
Right (Compose a) -> Right . ex' <$> a
Left err -> Left err <$ s''
exFinal <$> runStop tac
_ ->
error "top level Run"
inScope :: resource -> InterpreterFor (Scoped param (effect !! ei) !! eo) r1
inScope resource =
interpretWeaving \ (Weaving (Resumable (Weaving inner s' dist' ex' ins')) s'' dist'' ex'' ins'') -> case inner of
Run (Resumable (Weaving effect s dist ex ins)) ->
exFinal <$> runStop @ei (tac (scopedHandler resource effect))
where
tac =
runTactics
(Compose (Compose (s <$ s') <$ s''))
(raise . raise . inScope resource . fmap Compose . fmap (fmap Compose) . dist'' . fmap dist' . fmap (fmap dist . getCompose) . getCompose)
(ins <=< ins' . getCompose <=< ins'' . getCompose)
(raise . inScope resource . fmap Compose . fmap (fmap Compose) . dist'' . fmap dist' . fmap (fmap dist . getCompose) . getCompose)
exFinal = \case
Right (Compose fffa) ->
ex'' $ fffa <&> Right . \ (Compose ffa) ->
ex' (Right . ex <$> ffa)
Left err ->
ex'' (Right (ex' (Left err <$ s')) <$ s'')
InScope param main -> do
let
ma =
raise $ restack (extendMembershipLeft (singList @(Stop eo : extra))) $ withResource param \ resource' ->
Compose <$> inScope resource' (dist'' (dist' (main <$ s') <$ s''))
tac =
runTactics
(Compose (s' <$ s''))
(raise . raise . inScope resource . fmap Compose . dist'' . fmap dist' . getCompose)
(ins' <=< ins'' . getCompose)
(raise . inScope resource . fmap Compose . dist'' . fmap dist' . getCompose)
ma
exFinal = ex'' . \case
Right (Compose a) -> Right . ex' <$> a
Left err -> Left err <$ s''
exFinal <$> runStop tac
-- |Combined interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects that are
-- interpreted by the resource allocator.
-- In this variant, both the handler and the scope may send different errors via 'Stop', encoding the concept that the
-- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,
-- continuing the scope execution on resumption.
interpretScopedRWith ::
∀ extra param resource effect eo ei r r1 .
r1 ~ (extra ++ Stop eo : r) =>
r1 ~ ((extra ++ '[Stop eo]) ++ r) =>
KnownList extra =>
KnownList (extra ++ '[Stop eo]) =>
(∀ x . param -> (resource -> Sem r1 x) -> Sem (Stop eo : r) x) ->
(∀ r0 x . resource -> effect (Sem r0) x -> Sem (Stop ei : r1) x) ->
InterpreterFor (Scoped param (effect !! ei) !! eo) r
interpretScopedRWith withResource scopedHandler =
interpretScopedRWithH @extra withResource \ r e -> liftT (scopedHandler r e)
-- |Combined interpreter for 'Scoped' and 'Resumable' that allows the handler to use additional effects that are
-- interpreted by the resource allocator.
-- - Both the handler and the scope may send different errors via 'Stop', encoding the concept that the
-- resource allocation may fail to prevent the scope from being executed, and each individual scoped action may fail,
-- continuing the scope execution on resumption.
-- - The resource allocator is a plain action.
interpretScopedRWith_ ::
∀ extra param effect eo ei r r1 .
r1 ~ (extra ++ Stop eo : r) =>
r1 ~ ((extra ++ '[Stop eo]) ++ r) =>
KnownList extra =>
KnownList (extra ++ '[Stop eo]) =>
(∀ x . param -> Sem r1 x -> Sem (Stop eo : r) x) ->
(∀ r0 x . effect (Sem r0) x -> Sem (Stop ei : r1) x) ->
InterpreterFor (Scoped param (effect !! ei) !! eo) r
interpretScopedRWith_ extra scopedHandler =
interpretScopedRWith @extra (\ p f -> extra p (f ())) (const scopedHandler)