effectful-core-1.0.0.0: src/Effectful/Error/Dynamic.hs
-- | The dynamically dispatched variant of the 'Error' effect.
--
-- /Note:/ unless you plan to change interpretations at runtime, it's
-- recommended to use the statically dispatched variant,
-- i.e. "Effectful.Error.Static".
module Effectful.Error.Dynamic
( -- * Effect
Error(..)
-- ** Handlers
, runError
, runErrorNoCallStack
-- ** Operations
, throwError
, catchError
, handleError
, tryError
-- * Re-exports
, E.HasCallStack
, E.CallStack
, E.getCallStack
, E.prettyCallStack
) where
import Effectful
import Effectful.Dispatch.Dynamic
import qualified Effectful.Error.Static as E
-- | Provide the ability to handle errors of type @e@.
data Error e :: Effect where
ThrowError :: e -> Error e m a
CatchError :: m a -> (E.CallStack -> e -> m a) -> Error e m a
type instance DispatchOf (Error e) = Dynamic
-- | Handle errors of type @e@ (via "Effectful.Error.Static").
runError
:: Eff (Error e : es) a
-> Eff es (Either (E.CallStack, e) a)
runError = reinterpret E.runError $ \env -> \case
ThrowError e -> E.throwError e
CatchError m h -> localSeqUnlift env $ \unlift -> do
E.catchError (unlift m) (\cs -> unlift . h cs)
-- | Handle errors of type @e@ (via "Effectful.Error.Static"). In case of an
-- error discard the 'E.CallStack'.
runErrorNoCallStack
:: Eff (Error e : es) a
-> Eff es (Either e a)
runErrorNoCallStack = fmap (either (Left . snd) Right) . runError
-- | Throw an error of type @e@.
throwError
:: (HasCallStack, Error e :> es)
=> e
-- ^ The error.
-> Eff es a
throwError = send . ThrowError
-- | Handle an error of type @e@.
catchError
:: (HasCallStack, Error e :> es)
=> Eff es a
-- ^ The inner computation.
-> (E.CallStack -> e -> Eff es a)
-- ^ A handler for errors in the inner computation.
-> Eff es a
catchError m = send . CatchError m
-- | The same as @'flip' 'catchError'@, which is useful in situations where the
-- code for the handler is shorter.
handleError
:: Error e :> es
=> (E.CallStack -> e -> Eff es a)
-- ^ A handler for errors in the inner computation.
-> Eff es a
-- ^ The inner computation.
-> Eff es a
handleError = flip catchError
-- | Similar to 'catchError', but returns an 'Either' result which is a 'Right'
-- if no error was thrown and a 'Left' otherwise.
tryError
:: (HasCallStack, Error e :> es)
=> Eff es a
-- ^ The inner computation.
-> Eff es (Either (E.CallStack, e) a)
tryError m = (Right <$> m) `catchError` \es e -> pure $ Left (es, e)