packages feed

in-other-words-0.2.0.0: src/Control/Effect/Internal/Exceptional.hs

{-# LANGUAGE DerivingVia #-}
{-# OPTIONS_HADDOCK not-home #-}
module Control.Effect.Internal.Exceptional where

import Control.Effect
import Control.Effect.Error
import Control.Effect.ErrorIO
import Control.Effect.Union

import Control.Effect.Carrier

import Control.Effect.Internal.Utils

-- For coercion purposes
import Control.Monad.Trans.Except
import Control.Effect.Internal.Error
import Control.Effect.Carrier.Internal.Interpret
import Control.Effect.Carrier.Internal.Intro
import Control.Effect.Carrier.Internal.Compose


-- | An effect that allows for the safe use of an effect @eff@ that may
-- throw exceptions of the type @exc@ by forcing the user to eventually
-- catch those exceptions at some point of the program.
--
-- The main combinator of 'Exceptional' is 'Control.Effect.Exceptional.catching'.
--
-- __This could be unsafe in the presence of 'Control.Effect.Conc.Conc'__.
-- If you use 'Control.Effect.Exceptional.catching' on a computation that:
--
-- * Spawns an asynchronous computation
-- * Throws an exception inside the asynchronous computation from a use of @eff@
-- * Returns the 'Control.Effect.Conc.Async' of that asynchronous computation
--
-- Then 'Control.Effect.Conc.wait'ing on that 'Control.Effect.Conc.Async'
-- outside of the 'Control.Effect.Exceptional.catching' will throw that exception
-- without it being caught.
newtype Exceptional eff exc m a = Exceptional (Union '[eff, Catch exc] m a)

-- | A particularly useful specialization of 'Exceptional', for gaining
-- restricted access to an @'Error' exc@ effect.
-- Main combinators are 'Control.Effect.Exceptional.catchSafe' and
-- 'Control.Effect.Exceptional.trySafe'.
type SafeError exc = Exceptional (Throw exc) exc

{-
"ExceptionallyC" can easily be implemented using Handler:

data ExceptionallyH exc

instance ( Eff (Exceptional eff exc) m
         , RepresentationalEff eff
         )
      => Handler (ExceptionallH exc) eff m where where
  effHandler e = send $ Exceptionally $ inj e

type ExceptionallyC eff exc = InterpretC (ExceptionallH exc) eff

catching :: forall eff exc m a
          . ( Eff (Exceptional eff exc) m
            , RepresentationalEff eff
            )
         => ExceptionallyC exc eff m a
         -> (exc -> m a)
         -> m a
catching m h =
  send $ Exceptional @eff @exc $
    inj (Catch @exc (interpretViaHandler m) h)

We use a standalone carrier to hide the RepresentationalEff constraint,
which is just noise in this case.
-}

newtype ExceptionallyC (eff :: Effect) (exc :: *) m a = ExceptionallyC {
    unExceptionallyC :: m a
  }
  deriving ( Functor, Applicative, Monad
           , Alternative, MonadPlus
           , MonadFix, MonadFail, MonadIO
           , MonadThrow, MonadCatch, MonadMask
           , MonadBase b, MonadBaseControl b
           )
  deriving (MonadTrans, MonadTransControl) via IdentityT

instance Eff (Exceptional eff exc) m
      => Carrier (ExceptionallyC eff exc m) where
  type Derivs (ExceptionallyC eff exc m) = Catch exc ': eff ': Derivs m
  type Prims  (ExceptionallyC eff exc m) = Prims m

  algPrims = coerce (algPrims @m)
  {-# INLINEABLE algPrims #-}

  reformulate n alg =
    powerAlg (
    powerAlg' (
      reformulate (n .# lift) alg
    ) $ \e ->
      reformulate (n .# lift) alg $ inj $
        Exceptional @eff @exc (Union Here e)
    ) $ \e ->
      reformulate (n .# lift) alg $ inj $
        Exceptional @eff @exc (Union (There Here) e)
  {-# INLINEABLE reformulate #-}

  algDerivs =
    powerAlg (
    powerAlg' (
      coerce (algDerivs @m)
    ) $ \e ->
      coerceAlg (algDerivs @m) $ inj $ Exceptional @eff @exc (Union Here e)
    ) $ \e ->
      coerceAlg (algDerivs @m) $ inj $ Exceptional @eff @exc (Union (There Here) e)
  {-# INLINEABLE algDerivs #-}

data ExceptionalH

instance ( Member eff (Derivs m)
         , Eff (Catch exc) m
         )
      => Handler ExceptionalH (Exceptional eff exc) m where
  -- Explicit pattern mathing and use of 'algDerivs' instead of using
  -- 'decomp' and 'send' so that we don't introduce the
  -- RepresentationalEff constraint.
  effHandler (Exceptional e) = case e of
    Union Here eff             -> algDerivs (Union membership eff)
    Union (There Here) eff     -> algDerivs (Union membership eff)
    Union (There (There pr)) _ -> absurdMember pr
  {-# INLINEABLE effHandler #-}

type ExceptionalC eff exc = InterpretC ExceptionalH (Exceptional eff exc)

type SafeErrorToErrorC exc = ExceptionalC (Throw exc) exc

newtype SafeErrorC exc m a = SafeErrorC {
    unSafeErrorC ::
        IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
      ( SafeErrorToErrorC exc
      ( ErrorC exc
      ( m
      ))) a
  } deriving ( Functor, Applicative, Monad
             , Alternative, MonadPlus
             , MonadFix, MonadFail, MonadIO
             , MonadThrow, MonadCatch, MonadMask
             , MonadBase b, MonadBaseControl b
             )
    deriving (MonadTrans, MonadTransControl)
    via CompositionBaseT
     '[ IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
      , SafeErrorToErrorC exc
      , ErrorC exc
      ]

deriving instance (Carrier m, Threads (ExceptT exc) (Prims m))
               => Carrier (SafeErrorC exc m)

newtype SafeErrorToIOC' s s' exc m a = SafeErrorToIOC' {
    unSafeErrorToIOC' ::
        IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
      ( SafeErrorToErrorC exc
      ( ErrorToIOC' s s' exc
      ( m
      ))) a
  } deriving ( Functor, Applicative, Monad
             , Alternative, MonadPlus
             , MonadFix, MonadFail, MonadIO
             , MonadThrow, MonadCatch, MonadMask
             , MonadBase b, MonadBaseControl b
             )
    deriving (MonadTrans, MonadTransControl)
    via CompositionBaseT
     '[ IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
      , SafeErrorToErrorC exc
      , ErrorToIOC' s s' exc
      ]

deriving instance ( Eff (Embed IO) m, MonadCatch m
                  , ReifiesErrorHandler s s' exc (ErrorIOToIOC m)
                  )
               => Carrier (SafeErrorToIOC' s s' exc m)

type SafeErrorToIOC e m a =
     forall s s'
   . ReifiesErrorHandler s s' e (ErrorIOToIOC m)
  => SafeErrorToIOC' s s' e m a

newtype SafeErrorToErrorIOC' s s' exc m a = SafeErrorToErrorIOC' {
    unSafeErrorToErrorIOC' ::
        IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
      ( SafeErrorToErrorC exc
      ( InterpretErrorC' s s' exc
      ( m
      ))) a
  } deriving ( Functor, Applicative, Monad
             , Alternative, MonadPlus
             , MonadFix, MonadFail, MonadIO
             , MonadThrow, MonadCatch, MonadMask
             , MonadBase b, MonadBaseControl b
             )
    deriving (MonadTrans, MonadTransControl)
    via CompositionBaseT
     '[ IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
      , SafeErrorToErrorC exc
      , InterpretErrorC' s s' exc
      ]

deriving instance (Carrier m, ReifiesErrorHandler s s' exc m)
               => Carrier (SafeErrorToErrorIOC' s s' exc m)

type SafeErrorToErrorIOC e m a =
     forall s s'
   . ReifiesErrorHandler s s' e m
  => SafeErrorToErrorIOC' s s' e m a

newtype SafeErrorToIOSimpleC exc m a = SafeErrorToIOSimpleC {
    unSafeErrorToIOSimpleC ::
        IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
      ( SafeErrorToErrorC exc
      ( ErrorToIOSimpleC exc
      ( m
      ))) a
  } deriving ( Functor, Applicative, Monad
             , Alternative, MonadPlus
             , MonadFix, MonadFail, MonadIO
             , MonadThrow, MonadCatch, MonadMask
             , MonadBase b, MonadBaseControl b
             )
    deriving MonadTrans
    via CompositionBaseT
     '[ IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
      , SafeErrorToErrorC exc
      , ErrorToIOSimpleC exc
      ]

deriving instance ( Eff (Embed IO) m, MonadCatch m
                  , Threaders '[ReaderThreads] m p
                  )
               => Carrier (SafeErrorToIOSimpleC e m)


newtype SafeErrorToErrorIOSimpleC exc m a = SafeErrorToErrorIOSimpleC {
    unSafeErrorToErrorIOSimpleC ::
        IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
      ( SafeErrorToErrorC exc
      ( InterpretErrorSimpleC exc
      ( m
      ))) a
  } deriving ( Functor, Applicative, Monad
             , Alternative, MonadPlus
             , MonadFix, MonadFail, MonadIO
             , MonadThrow, MonadCatch, MonadMask
             , MonadBase b, MonadBaseControl b
             )
    deriving MonadTrans
    via CompositionBaseT
     '[ IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
      , SafeErrorToErrorC exc
      , InterpretErrorSimpleC exc
      ]

deriving instance (Carrier m , Threaders '[ReaderThreads] m p)
               => Carrier (SafeErrorToErrorIOSimpleC e m)