Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
Synopsis
- data Stop e :: Effect where
- stop :: forall e r a. MemberWithError (Stop e) r => e -> Sem r a
- type (!!) eff err = Resumable err eff
- data Resumable err eff :: Effect
- resume :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> (err -> Sem r a) -> Sem r a
- (!!) :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> (err -> Sem r a) -> Sem r a
- interpretResumable :: forall (err :: Type) (eff :: Effect) r. FirstOrder eff "interpretResumable" => (forall x r0. eff (Sem r0) x -> Sem (Stop err ': r) x) -> InterpreterFor (Resumable err eff) r
- interpretResumableH :: forall (err :: Type) (eff :: Effect) (r :: EffectRow). (forall x r0. eff (Sem r0) x -> Tactical (Resumable err eff) (Sem r0) (Stop err ': r) x) -> InterpreterFor (Resumable err eff) r
- resumable :: forall (err :: Type) (eff :: Effect) (r :: EffectRow). InterpreterFor eff (Stop err ': r) -> InterpreterFor (Resumable err eff) r
- raiseResumable :: forall (err :: Type) (eff :: Effect) (r :: EffectRow). InterpreterTrans (Resumable err eff) eff r
- resumableIO :: forall (err :: Type) (eff :: Effect) (r :: EffectRow). Exception (StopExc err) => Member (Final IO) r => InterpreterFor eff (Stop err ': r) -> InterpreterFor (Resumable err eff) r
- resumableOr :: forall (err :: Type) (eff :: Effect) unhandled handled r. Member (Error unhandled) r => (err -> Either unhandled handled) -> InterpreterFor eff (Stop err ': r) -> InterpreterFor (Resumable handled eff) r
- resumeAs :: forall err eff r a. Member (Resumable err eff) r => a -> Sem (eff ': r) a -> Sem r a
- (<!) :: forall err eff r a. Member (Resumable err eff) r => a -> Sem (eff ': r) a -> Sem r a
- (!>) :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> a -> Sem r a
- resumeWith :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> Sem r a -> Sem r a
- (!>>) :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> Sem r a -> Sem r a
- resumingWith :: forall err eff r a. Member (Resumable err eff) r => Sem r a -> Sem (eff ': r) a -> Sem r a
- (<<!) :: forall err eff r a. Member (Resumable err eff) r => Sem r a -> Sem (eff ': r) a -> Sem r a
- resume_ :: forall err eff r. Member (Resumable err eff) r => Sem (eff ': r) () -> Sem r ()
- resumeHoist :: forall err eff err' r a. Members [Resumable err eff, Stop err'] r => (err -> err') -> Sem (eff ': r) a -> Sem r a
- resumeHoistAs :: forall err eff err' r. Members [Resumable err eff, Stop err'] r => err' -> InterpreterFor eff r
- resuming :: forall err eff r a. Member (Resumable err eff) r => (err -> Sem r a) -> Sem (eff ': r) a -> Sem r a
- resumeHoistError :: forall err eff err' r a. Members [Resumable err eff, Error err'] r => (err -> err') -> Sem (eff ': r) a -> Sem r a
- resumeHoistErrorAs :: forall err eff err' r a. Members [Resumable err eff, Error err'] r => err' -> Sem (eff ': r) a -> Sem r a
- restop :: forall err eff r. Members [Resumable err eff, Stop err] r => InterpreterFor eff r
- resumeEither :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> Sem r (Either err a)
- resumeOr :: forall err eff r a b. Member (Resumable err eff) r => Sem (eff ': r) a -> (a -> Sem r b) -> (err -> Sem r b) -> Sem r b
- resumingOr :: forall err eff r a b. Member (Resumable err eff) r => (err -> Sem r b) -> Sem (eff ': r) a -> (a -> Sem r b) -> Sem r b
- resumeError :: forall err eff r a. Members [Resumable err eff, Error err] r => Sem (eff ': r) a -> Sem r a
- resumableError :: forall (err :: Type) (eff :: Effect) r. InterpreterFor eff (Error err ': (Stop err ': r)) -> InterpreterFor (Resumable err eff) r
- resumableFor :: forall (err :: Type) (eff :: Effect) handled r. Member (Error err) r => (err -> Maybe handled) -> InterpreterFor eff (Stop err ': r) -> InterpreterFor (Resumable handled eff) r
- runAsResumable :: forall (err :: Type) (eff :: Effect) r. Members [Resumable err eff, Stop err] r => InterpreterFor eff r
- catchResumable :: forall (err :: Type) (eff :: Effect) handled r. Members [eff, Error err] r => (err -> Maybe handled) -> InterpreterFor (Resumable handled eff) r
- runStop :: Sem (Stop e ': r) a -> Sem r (Either e a)
- stopToIOFinal :: Exception (StopExc e) => Member (Final IO) r => Sem (Stop e ': r) a -> Sem r (Either e a)
- stopEitherWith :: Member (Stop err') r => (err -> err') -> Either err a -> Sem r a
- stopEither :: Member (Stop err) r => Either err a -> Sem r a
- stopNote :: Member (Stop err) r => err -> Maybe a -> Sem r a
- stopOnError :: Member (Stop err) r => Sem (Error err ': r) a -> Sem r a
- stopOnErrorWith :: Member (Stop err') r => (err -> err') -> Sem (Error err ': r) a -> Sem r a
- stopToError :: Member (Error err) r => Sem (Stop err ': r) a -> Sem r a
- mapStop :: forall e e' r a. Member (Stop e') r => (e -> e') -> Sem (Stop e ': r) a -> Sem r a
- showStop :: forall e r a. Show e => Member (Stop Text) r => Sem (Stop e ': r) a -> Sem r a
Introduction
This library provides the Polysemy effects Resumable
and Stop
for the purpose of safely connecting throwing and
catching errors across different interpreters.
Consider the effect:
data Stopper :: Effect where StopBang :: Stopper m () StopBoom :: Stopper m () makeSem ''Stopper data Boom = Boom { unBoom :: Text } | Bang { unBang :: Int } deriving (Eq, Show) interpretStopper :: Member (Error Boom) r => InterpreterFor Stopper r interpretStopper = interpret \case StopBang -> throw (Bang 13) StopBoom -> throw (Boom "ouch")
If we want to use Stopper
in the interpreter of another effect, we have no way of knowing about the errors thrown
by its interpreter, even though we can catch Boom
!
This library makes the connection explicit by changing Error
to Stop
and wrapping Stopper
in
Resumable
when using it in an effect stack:
data Stop e :: Effect where Source #
An effect similar to Error
without the ability to be caught.
Used to signal that an error is supposed to be expected by dependent programs.
interpretStopper :: Member (Stop Boom) r => InterpreterFor Stopper r interpretStopper = interpret \case StopBang -> stop (Bang 13) StopBoom -> stop (Boom "ouch")
Instances
type DefiningModule Stop Source # | |
Defined in Polysemy.Resume.Data.Stop |
type (!!) eff err = Resumable err eff Source #
Infix alias for Resumable
.
Member (Stopper !! Boom) r =>
data Resumable err eff :: Effect Source #
Effect that wraps another effect eff
, marking it as throwing errors of type err
using
Stop
.
Resuming a Stopped Computation
resume :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> (err -> Sem r a) -> Sem r a Source #
Execute the action of a regular effect eff
so that any error of type err
that maybe be thrown by the (unknown)
interpreter used for eff
will be caught here and handled by the handler
argument.
This is similar to catch
with the additional guarantee that the error will have to be explicitly
matched, therefore preventing accidental failure to handle an error and bubbling it up to main
.
It also imposes a membership of Resumable err eff
on the program, requiring the interpreter for eff
to be adapted
with resumable
.
data Resumer :: Effect where MainProgram :: Resumer m Int makeSem ''Resumer interpretResumer :: Member (Resumable Boom Stopper) r => InterpreterFor Resumer r interpretResumer = interpret \ MainProgram -> resume (192 <$ stopBang) \ _ -> pure 237
(!!) :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> (err -> Sem r a) -> Sem r a Source #
Operator version of resume
.
Since: 0.2.0.0
interpretResumable :: forall (err :: Type) (eff :: Effect) r. FirstOrder eff "interpretResumable" => (forall x r0. eff (Sem r0) x -> Sem (Stop err ': r) x) -> InterpreterFor (Resumable err eff) r Source #
Create an interpreter for
by supplying a handler function for Resumable
err effeff
, analogous to
interpret
.
If the handler throws errors with Stop
, they will be absorbed into Resumable
, to be caught by
resume
in a downstream interpreter.
interpretStopperResumable :: Member (Stop Boom) r => InterpreterFor Stopper r interpretStopperResumable = interpretResumable \case StopBang -> stop (Bang 13) StopBoom -> stop (Boom "ouch")
>>>
run $ interpretStopperResumable (interpretResumer mainProgram)
237
:: forall (err :: Type) (eff :: Effect) (r :: EffectRow). (forall x r0. eff (Sem r0) x -> Tactical (Resumable err eff) (Sem r0) (Stop err ': r) x) | This handler function has |
-> InterpreterFor (Resumable err eff) r |
Like interpretResumable
, but for higher-order effects.
resumable :: forall (err :: Type) (eff :: Effect) (r :: EffectRow). InterpreterFor eff (Stop err ': r) -> InterpreterFor (Resumable err eff) r Source #
Convert a bare interpreter for eff
, which (potentially) uses Stop
to signal errors, into an interpreter for
Resumable
.
Beware: This will display unsound behaviour if:
* the interpreter is wrapped with actions of another effect, as in:
interpretEff :: InterpreterFor Eff r
...
interpretEffResumable :: InterpreterFor (Resumable Text Eff) r
interpretEffResumable sem =
resumable (interpretEff (sem finally
releaseResources))
In this case, releaseResources
will be called after every use of Eff
in sem
, not after the entire thunk.
- the interpreter of a higher-order effect uses a different interpreter after using
runT
/bindT
. In this case, it will use the original interpreter instead.
If your use case matches one of these conditions, you'll need to use interpretResumable
.
>>>
run $ resumable interpretStopper (interpretResumer mainProgram)
237
raiseResumable :: forall (err :: Type) (eff :: Effect) (r :: EffectRow). InterpreterTrans (Resumable err eff) eff r Source #
resumableIO :: forall (err :: Type) (eff :: Effect) (r :: EffectRow). Exception (StopExc err) => Member (Final IO) r => InterpreterFor eff (Stop err ': r) -> InterpreterFor (Resumable err eff) r Source #
Like resumable
, but use exceptions instead of ExceptT
.
Partial Handlers
In some cases, the errors thrown by an interpreter contain details about the implementation, which we might want to
hide from dependents; or it may throw fatal errors we don't want to handle at all.
For this purpose, we can create partial Resumable
s by transforming errors before handling them:
resumableOr :: forall (err :: Type) (eff :: Effect) unhandled handled r. Member (Error unhandled) r => (err -> Either unhandled handled) -> InterpreterFor eff (Stop err ': r) -> InterpreterFor (Resumable handled eff) r Source #
Convert an interpreter for eff
that throws errors of type err
into a Resumable
, but limiting the errors
handled by consumers to the type handled
, which rethrowing Error
s of type unhandled
.
The function canHandle
determines how the errors are split.
newtype Blip = Blip { unBlip :: Int } deriving (Eq, Show) bangOnly :: Boom -> Either Text Blip bangOnly = \case Bang n -> Right (Blip n) Boom msg -> Left msg interpretResumerPartial :: Member (Resumable Blip Stopper) r => InterpreterFor Resumer r interpretResumerPartial = interpret \ MainProgram -> resume (192 <$ stopBang) \ (Blip num) -> pure (num * 3)
>>>
runError (resumableFor bangOnly interpretStopper (interpretResumerPartial mainProgram))
Right 39
Various Combinators
resumeAs :: forall err eff r a. Member (Resumable err eff) r => a -> Sem (eff ': r) a -> Sem r a Source #
Variant of resume
that unconditionally recovers with a constant value.
(<!) :: forall err eff r a. Member (Resumable err eff) r => a -> Sem (eff ': r) a -> Sem r a Source #
Operator version of resumeAs
.
Since: 0.2.0.0
(!>) :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> a -> Sem r a Source #
resumeWith :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> Sem r a -> Sem r a Source #
Variant of resume
that unconditionally recovers with an action.
Since: 0.2.0.0
(!>>) :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> Sem r a -> Sem r a Source #
Operator variant of resumeWith
.
Since: 0.2.0.0
resumingWith :: forall err eff r a. Member (Resumable err eff) r => Sem r a -> Sem (eff ': r) a -> Sem r a Source #
Variant of resuming
that unconditionally recovers with an action.
Since: 0.2.0.0
(<<!) :: forall err eff r a. Member (Resumable err eff) r => Sem r a -> Sem (eff ': r) a -> Sem r a Source #
Operator variant of resumingWith
.
Since: 0.2.0.0
resume_ :: forall err eff r. Member (Resumable err eff) r => Sem (eff ': r) () -> Sem r () Source #
Convenience specialization of resume
that silently discards errors for void programs.
resumeHoist :: forall err eff err' r a. Members [Resumable err eff, Stop err'] r => (err -> err') -> Sem (eff ': r) a -> Sem r a Source #
resumeHoistAs :: forall err eff err' r. Members [Resumable err eff, Stop err'] r => err' -> InterpreterFor eff r Source #
Variant of resumeHoist
that uses a constant value.
resuming :: forall err eff r a. Member (Resumable err eff) r => (err -> Sem r a) -> Sem (eff ': r) a -> Sem r a Source #
Flipped variant of resume
.
resumeHoistError :: forall err eff err' r a. Members [Resumable err eff, Error err'] r => (err -> err') -> Sem (eff ': r) a -> Sem r a Source #
resumeHoistErrorAs :: forall err eff err' r a. Members [Resumable err eff, Error err'] r => err' -> Sem (eff ': r) a -> Sem r a Source #
Variant of resumeHoistError
that uses the unchanged error.
restop :: forall err eff r. Members [Resumable err eff, Stop err] r => InterpreterFor eff r Source #
Variant of resumeHoist
that uses the unchanged error.
resumeEither :: forall err eff r a. Member (Resumable err eff) r => Sem (eff ': r) a -> Sem r (Either err a) Source #
resumeOr :: forall err eff r a b. Member (Resumable err eff) r => Sem (eff ': r) a -> (a -> Sem r b) -> (err -> Sem r b) -> Sem r b Source #
Variant of resume
that takes a branch for error and success.
This allows the success branch to contain other resumptions.
Since: 0.2.0.0
resumingOr :: forall err eff r a b. Member (Resumable err eff) r => (err -> Sem r b) -> Sem (eff ': r) a -> (a -> Sem r b) -> Sem r b Source #
Variant of resuming
that takes a branch for error and success.
This allows the success branch to contain other resumptions.
Since: 0.2.0.0
resumeError :: forall err eff r a. Members [Resumable err eff, Error err] r => Sem (eff ': r) a -> Sem r a Source #
Variant of resumeHoistError
that uses the unchanged error.
resumableError :: forall (err :: Type) (eff :: Effect) r. InterpreterFor eff (Error err ': (Stop err ': r)) -> InterpreterFor (Resumable err eff) r Source #
resumableFor :: forall (err :: Type) (eff :: Effect) handled r. Member (Error err) r => (err -> Maybe handled) -> InterpreterFor eff (Stop err ': r) -> InterpreterFor (Resumable handled eff) r Source #
Variant of resumableOr
that uses Maybe
and rethrows the original error.
runAsResumable :: forall (err :: Type) (eff :: Effect) r. Members [Resumable err eff, Stop err] r => InterpreterFor eff r Source #
Interpret an effect eff
by wrapping it in Resumable
and Stop
and leaving the rest up to the user.
catchResumable :: forall (err :: Type) (eff :: Effect) handled r. Members [eff, Error err] r => (err -> Maybe handled) -> InterpreterFor (Resumable handled eff) r Source #
Reinterpreting variant of resumableFor
.
stopToIOFinal :: Exception (StopExc e) => Member (Final IO) r => Sem (Stop e ': r) a -> Sem r (Either e a) Source #
Run Stop
by throwing exceptions.
stopEitherWith :: Member (Stop err') r => (err -> err') -> Either err a -> Sem r a Source #
Stop if the argument is Left
, transforming the error with f
.
stopNote :: Member (Stop err) r => err -> Maybe a -> Sem r a Source #
Stop with the supplied error if the argument is Nothing
.
stopOnErrorWith :: Member (Stop err') r => (err -> err') -> Sem (Error err ': r) a -> Sem r a Source #