content: {-# LANGUAGE BlockArguments #-}
  module Control.Effect.Error
    ( -- * Effects
      Throw(..)
    , Catch(..)
    , Error
  
      -- * Actions
    , throw
    , catch
    , try
    , catchJust
    , tryJust
    , note
    , fromEither
  
      -- * Main Interpreters
    , runThrow
  
    , runError
  
    , errorToIO
  
    , errorToIOAsExc
  
      -- * Other interpreters
    , errorToErrorIO
  
    , errorToErrorIOAsExc
  
    , throwToThrow
    , catchToError
    , errorToError
  
      -- * Simple variants
    , errorToIOSimple
    , errorToErrorIOSimple
  
    , throwToThrowSimple
    , catchToErrorSimple
    , errorToErrorSimple
  
      -- * Threading constraints
    , ErrorThreads
  
      -- * MonadCatch
    , C.MonadCatch
  
      -- * Carriers
    , ThrowC
    , ErrorC
    , ErrorToIOC
    , ErrorToIOC'
    , ReifiesErrorHandler
    , InterpretErrorC
    , InterpretErrorC'
    , ErrorToIOSimpleC
    , InterpretErrorSimpleC
    ) where
  
  import Data.Function
  import Data.Coerce
  
  import Control.Effect
  import Control.Effect.Type.Throw
  import Control.Effect.Type.Catch
  import Control.Effect.Internal.Error
  
  import qualified Control.Monad.Catch as C
  
  -- For coercion purposes
  import Control.Monad.Trans.Except
  import Control.Effect.Carrier.Internal.Interpret
  import Control.Effect.Carrier.Internal.Intro
  import Control.Effect.Internal.Utils
  
  
  throw :: Eff (Throw e) m => e -> m a
  throw = send .# Throw
  {-# INLINE throw #-}
  
  catch :: Eff (Catch e) m => m a -> (e -> m a) -> m a
  catch m h = send (Catch m h)
  {-# INLINE catch #-}
  
  try :: Eff (Catch e) m => m a -> m (Either e a)
  try m = fmap Right m `catch` (return . Left)
  {-# INLINE try #-}
  
  
  catchJust :: forall smallExc bigExc m a
             . Eff (Error bigExc) m
            => (bigExc -> Maybe smallExc)
            -> m a
            -> (smallExc -> m a)
            -> m a
  catchJust f m h = m `catch` \e -> maybe (throw e) h (f e)
  {-# INLINE catchJust #-}
  
  tryJust :: forall smallExc bigExc m a
           . Eff (Error bigExc) m
          => (bigExc -> Maybe smallExc)
          -> m a
          -> m (Either smallExc a)
  tryJust f m = fmap Right m &(catchJust f)$ (return . Left)
  {-# INLINE tryJust #-}
  
  note :: Eff (Throw e) m => e -> Maybe a -> m a
  note _ (Just a) = return a
  note e Nothing  = throw e
  {-# INLINE note #-}
  
  fromEither :: Eff (Throw e) m => Either e a -> m a
  fromEither = either throw pure
  {-# INLINE fromEither #-}
  
  -- | Run a 'Throw' effect purely.
  --
  -- Unlike 'runError', this does not provide the ability to catch exceptions.
  -- However, it also doesn't impose any primitive effects, meaning 'runThrow' doesn't
  -- restrict what interpreters are run before it.
  --
  -- @'Derivs' ('ThrowC' e m) = 'Throw' e ': 'Derivs' m@
  --
  -- @'Control.Effect.Primitive.Prims'  ('ThrowC' e m) = 'Control.Effect.Primitive.Prims' m@
  runThrow :: forall e m a p
            . ( Carrier m
              , Threaders '[ErrorThreads] m p
              )
           => ThrowC e m a
           -> m (Either e a)
  runThrow = coerce
  {-# INLINE runThrow #-}
  
  -- | Runs connected 'Throw' and 'Catch' effects -- i.e. 'Error' -- purely.
  --
  -- @'Derivs' ('ErrorC' e m) = 'Catch' e ': 'Throw' e ': 'Derivs' m@
  --
  -- @'Control.Effect.Primitive.Prims'  ('ErrorC' e m) = 'Control.Effect.Optional.Optional' ((->) e) ': 'Control.Effect.Primitive.Prims' m@
  runError :: forall e m a p
            . ( Carrier m
              , Threaders '[ErrorThreads] m p
              )
           => ErrorC e m a
           -> m (Either e a)
  runError = coerce
  {-# INLINE runError #-}
  
  
  -- | Transforms a @'Throw' smallExc@ effect into a @'Throw' bigExc@ effect,
  -- by providing a function to convert exceptions of the smaller exception type
  -- @smallExc@ to the larger exception type @bigExc@.
  --
  -- This has a higher-rank type, as it makes use of 'InterpretReifiedC'.
  -- __This makes 'throwToThrow' very difficult to use partially applied.__
  -- __In particular, it can't be composed using @'.'@.__
  --
  -- If performance is secondary, consider using the slower
  -- 'throwToThrowSimple', which doesn't have a higher-rank type.
  throwToThrow :: forall smallExc bigExc m a
                . Eff (Throw bigExc) m
               => (smallExc -> bigExc)
               -> InterpretReifiedC (Throw smallExc) m a
               -> m a
  throwToThrow to = interpret $ \case
    Throw e -> throw (to e)
  {-# INLINE throwToThrow #-}
  
  -- | Transforms a @'Catch' smallExc@ effect into an @'Error' bigExc@ effect, by
  -- providing a function that identifies when exceptions of the larger exception type
  -- @bigExc@ correspond to exceptions of the smaller exception type @smallExc@.
  --
  -- This has a higher-rank type, as it makes use of 'InterpretReifiedC'.
  -- __This makes 'catchToError' very difficult to use partially applied.__
  -- __In particular, it can't be composed using @'.'@.__
  --
  -- If performance is secondary, consider using the slower
  -- 'catchToErrorSimple', which doesn't have a higher-rank type.
  catchToError :: forall smallExc bigExc m a
                . Eff (Error bigExc) m
               => (bigExc -> Maybe smallExc)
               -> InterpretReifiedC (Catch smallExc) m a
               -> m a
  catchToError from = interpret $ \case
    Catch m h -> m &(catchJust from)$ h
  {-# INLINE catchToError #-}
  
  
  
  -- | Transforms connected 'Throw' and 'Catch' effects -- i.e. 'Error' --
  -- into another 'Error' effect by providing functions to convert
  -- between the two types of exceptions.
  --
  -- This has a higher-rank type, as it makes use of 'InterpretErrorC'.
  -- __This makes 'errorToError' very difficult to use partially applied.__
  -- __In particular, it can't be composed using @'.'@.__
  --
  -- If performance is secondary, consider using the slower
  -- 'errorToErrorSimple', which doesn't have a higher-rank type.
  errorToError :: forall smallExc bigExc m a
                . Eff (Error bigExc) m
               => (smallExc -> bigExc)
               -> (bigExc -> Maybe smallExc)
               -> InterpretErrorC smallExc m a
               -> m a
  errorToError to from m0 =
      throwToThrow to
      -- can't use 'catchToError' directly,
      -- since it can't tell if it should use smallExc or bigExc for its 'Throw'.
      -- We fix that by using 'intro1'.
    $ interpret \case
        Catch m h -> m `catch` \e -> case from e of
          Just e' -> h e'
          Nothing -> intro1 $ throw e
    $ unInterpretErrorC'
    $ m0
  {-# INLINE errorToError #-}
  
  -- | Transforms a @'Throw' smallExc@ effect into a @'Throw' bigExc@ effect,
  -- by providing a function to convert exceptions of the smaller exception type
  -- @smallExc@ to the larger exception type @bigExc@.
  --
  -- This is a less performant version of 'throwToThrow' that doesn't have
  -- a higher-rank type, making it much easier to use partially applied.
  throwToThrowSimple :: forall smallExc bigExc m a p
                      . ( Eff (Throw bigExc) m
                        , Threaders '[ReaderThreads] m p
                        )
                     => (smallExc -> bigExc)
                     -> InterpretSimpleC (Throw smallExc) m a
                     -> m a
  throwToThrowSimple to = interpretSimple $ \case
    Throw e -> throw (to e)
  {-# INLINE throwToThrowSimple #-}
  
  -- | Transforms a @'Catch' smallExc@ effect into an @'Error' bigExc@ effect, by
  -- providing a function that identifies when exceptions of the larger exception type
  -- @bigExc@ correspond to exceptions of the smaller exception type @smallExc@.
  --
  -- This is a less performant version of 'catchToError' that doesn't have
  -- a higher-rank type, making it much easier to use partially applied.
  catchToErrorSimple :: forall smallExc bigExc m a p
                      . ( Eff (Error bigExc) m
                        , Threaders '[ReaderThreads] m p
                        )
                     => (bigExc -> Maybe smallExc)
                     -> InterpretSimpleC (Catch smallExc) m a
                     -> m a
  catchToErrorSimple from = interpretSimple $ \case
    Catch m h -> catchJust from m h
  {-# INLINE catchToErrorSimple #-}
  
  -- | Transforms connected 'Throw' and 'Catch' effects -- i.e. 'Error' --
  -- into another 'Error' effect by providing functions to convert
  -- between the two types of exceptions.
  --
  -- This is a less performant version of 'errorToError' that doesn't have
  -- a higher-rank type, making it much easier to use partially applied.
  errorToErrorSimple :: forall smallExc bigExc m a p
                      . ( Eff (Error bigExc) m
                        , Threaders '[ReaderThreads] m p
                        )
                     => (smallExc -> bigExc)
                     -> (bigExc -> Maybe smallExc)
                     -> InterpretErrorSimpleC smallExc m a
                     -> m a
  errorToErrorSimple to from =
       throwToThrowSimple to
    .  interpretSimple \case
         Catch m h -> intro1 $ catchJust from (lift m) (lift #. h)
    .# unInterpretErrorSimpleC
  {-# INLINE errorToErrorSimple #-}
  