polysemy-resume-0.1.0.2: lib/Polysemy/Resume/Resume.hs
module Polysemy.Resume.Resume where
import Polysemy (raiseUnder2, raiseUnder)
import Polysemy.Error (throw)
import Polysemy.Resume.Data.Resumable (Resumable)
import Polysemy.Resume.Data.Stop (Stop, stop)
import Polysemy.Resume.Resumable (runAsResumable)
import Polysemy.Resume.Stop (runStop)
-- |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 'Polysemy.Error.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 'Polysemy.Resume.Resumable.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
-- @
resume ::
∀ err eff r a .
Member (Resumable err eff) r =>
Sem (eff : r) a ->
(err -> Sem r a) ->
Sem r a
resume sem handler =
either handler pure =<< runStop (runAsResumable @err (raiseUnder sem))
{-# INLINE resume #-}
-- Reinterpreting version of 'resume'.
resumeRe ::
∀ err eff r a .
Sem (eff : r) a ->
(err -> Sem (Resumable err eff : r) a) ->
Sem (Resumable err eff : r) a
resumeRe sem handler =
either handler pure =<< runStop (runAsResumable @err (raiseUnder2 sem))
{-# INLINE resumeRe #-}
-- |Flipped variant of 'resume'.
resuming ::
∀ err eff r a .
Member (Resumable err eff) r =>
(err -> Sem r a) ->
Sem (eff : r) a ->
Sem r a
resuming =
flip resume
{-# INLINE resuming #-}
-- |Flipped variant of 'resumeRe'.
resumingRe ::
∀ err eff r a .
(err -> Sem (Resumable err eff : r) a) ->
Sem (eff : r) a ->
Sem (Resumable err eff : r) a
resumingRe =
flip resumeRe
{-# INLINE resumingRe #-}
-- |Variant of 'resume' that unconditionally recovers with a constant value.
resumeAs ::
∀ err eff r a .
Member (Resumable err eff) r =>
a ->
Sem (eff : r) a ->
Sem r a
resumeAs a =
resuming @err \ _ -> pure a
{-# INLINE resumeAs #-}
-- |Convenience specialization of 'resume' that silently discards errors for void programs.
resume_ ::
∀ err eff r .
Member (Resumable err eff) r =>
Sem (eff : r) () ->
Sem r ()
resume_ =
resumeAs @err ()
-- |Variant of 'resume' that propagates the error to another 'Stop' effect after applying a function.
resumeHoist ::
∀ err eff err' r a .
Members [Resumable err eff, Stop err'] r =>
(err -> err') ->
Sem (eff : r) a ->
Sem r a
resumeHoist f =
resuming (stop . f)
{-# INLINE resumeHoist #-}
-- |Variant of 'resumeHoist' that uses a constant value.
resumeHoistAs ::
∀ err eff err' r .
Members [Resumable err eff, Stop err'] r =>
err' ->
InterpreterFor eff r
resumeHoistAs err =
resumeHoist @err (const err)
{-# INLINE resumeHoistAs #-}
-- |Variant of 'resumeHoist' that uses the unchanged error.
restop ::
∀ err eff r .
Members [Resumable err eff, Stop err] r =>
InterpreterFor eff r
restop =
resumeHoist @err id
{-# INLINE restop #-}
-- |Variant of 'restop' that immediately produces an 'Either'.
resumeEither ::
∀ err eff r a .
Member (Resumable err eff) r =>
Sem (eff : r) a ->
Sem r (Either err a)
resumeEither =
runStop . restop @err . raiseUnder
-- |Variant of 'resume' that propagates the error to an 'Error' effect after applying a function.
resumeHoistError ::
∀ err eff err' r a .
Members [Resumable err eff, Error err'] r =>
(err -> err') ->
Sem (eff : r) a ->
Sem r a
resumeHoistError f =
resuming (throw . f)
{-# INLINE resumeHoistError #-}
-- |Variant of 'resumeHoistError' that uses the unchanged error.
resumeHoistErrorAs ::
∀ err eff err' r a .
Members [Resumable err eff, Error err'] r =>
err' ->
Sem (eff : r) a ->
Sem r a
resumeHoistErrorAs err =
resumeHoistError @err (const err)
{-# INLINE resumeHoistErrorAs #-}
-- |Variant of 'resumeHoistError' that uses the unchanged error.
resumeError ::
∀ err eff r a .
Members [Resumable err eff, Error err] r =>
Sem (eff : r) a ->
Sem r a
resumeError =
resumeHoistError @err id
{-# INLINE resumeError #-}