{-# LANGUAGE DerivingVia #-}
module Control.Effect.Exceptional
(
Exceptional
, SafeError
, catching
, trying
, throwing
, catchSafe
, trySafe
, runExceptional
, runExceptionalJust
, safeErrorToError
, runSafeError
, safeErrorToIO
, safeErrorToErrorIO
, runExceptionalJustSimple
, safeErrorToIOSimple
, safeErrorToErrorIOSimple
, ErrorThreads
, MonadCatch
, ExceptionallyC
, ExceptionalC
, SafeErrorToErrorC
, SafeErrorC
, SafeErrorToIOC'
, SafeErrorToIOC
, SafeErrorToErrorIOC'
, SafeErrorToErrorIOC
, SafeErrorToIOSimpleC
, SafeErrorToErrorIOSimpleC
) where
import Data.Coerce
import Data.Either
import Control.Effect
import Control.Effect.Error
import Control.Effect.ErrorIO
import Control.Effect.Union
import Control.Effect.Carrier
import Control.Effect.Internal.Utils
import Control.Monad.Trans.Identity
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
newtype Exceptional eff exc m a = Exceptional (Union '[eff, Catch exc] m a)
type SafeError exc = Exceptional (Throw exc) exc
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) = eff ': Catch exc ': 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 (There Here) e)
) $ \e ->
reformulate (n .# lift) alg $ inj $
Exceptional @eff @exc (Union Here e)
{-# INLINEABLE reformulate #-}
algDerivs =
powerAlg' (
powerAlg (
coerce (algDerivs @m)
) $ \e ->
coerceAlg (algDerivs @m) $ inj $ Exceptional @eff @exc (Union (There Here) e)
) $ \e ->
coerceAlg (algDerivs @m) $ inj $ Exceptional @eff @exc (Union Here e)
{-# INLINEABLE algDerivs #-}
catching :: forall eff exc m a
. Eff (Exceptional eff exc) m
=> ExceptionallyC eff exc m a
-> (exc -> m a)
-> m a
catching m h =
send $ Exceptional @eff @exc $
Union (There Here) (Catch (unExceptionallyC m) h)
{-# INLINE catching #-}
catchSafe :: forall exc m a
. Eff (SafeError exc) m
=> ExceptionallyC (Throw exc) exc m a
-> (exc -> m a)
-> m a
catchSafe = catching
{-# INLINE catchSafe #-}
trying :: forall eff exc m a
. Eff (Exceptional eff exc) m
=> ExceptionallyC eff exc m a
-> m (Either exc a)
trying m = fmap Right m `catching` (return . Left)
{-# INLINE trying #-}
trySafe :: forall exc m a
. Eff (SafeError exc) m
=> ExceptionallyC (Throw exc) exc m a
-> m (Either exc a)
trySafe = trying
{-# INLINE trySafe #-}
throwing :: forall eff exc m a
. Effs [Exceptional eff exc, Throw exc] m
=> ExceptionallyC eff exc m a
-> m a
throwing m = m `catching` throw
{-# INLINE throwing #-}
data ExceptionalH
instance ( Member eff (Derivs m)
, Eff (Catch exc) m
)
=> Handler ExceptionalH (Exceptional eff exc) m where
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
runExceptional :: forall eff exc m a
. ( Member eff (Derivs m)
, Eff (Catch exc) m
)
=> ExceptionalC eff exc m a
-> m a
runExceptional = interpretViaHandler
{-# INLINE runExceptional #-}
runExceptionalJust :: forall eff smallExc bigExc m a
. ( Member eff (Derivs m)
, Eff (Error bigExc) m
)
=> (bigExc -> Maybe smallExc)
-> InterpretReifiedC (Exceptional eff smallExc) m a
-> m a
runExceptionalJust from = interpret $ \(Exceptional e) -> case e of
Union Here eff -> algDerivs (Union membership eff)
Union (There pr) eff -> case extract (Union pr eff) of
Catch m h -> catchJust from m h
{-# INLINE runExceptionalJust #-}
runExceptionalJustSimple :: forall eff smallExc bigExc m a p
. ( Member eff (Derivs m)
, Eff (Error bigExc) m
, Threaders '[ReaderThreads] m p
)
=> (bigExc -> Maybe smallExc)
-> InterpretSimpleC (Exceptional eff smallExc) m a
-> m a
runExceptionalJustSimple from = interpretSimple $ \(Exceptional e) -> case e of
Union Here eff -> algDerivs (Union membership eff)
Union (There pr) eff -> case extract (Union pr eff) of
Catch m h -> catchJust from m h
{-# INLINE runExceptionalJustSimple #-}
safeErrorToError :: forall exc m a
. Eff (Error exc) m
=> SafeErrorToErrorC exc m a
-> m a
safeErrorToError = runExceptional
{-# INLINE safeErrorToError #-}
type SafeErrorC exc = CompositionC
'[ IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
, SafeErrorToErrorC exc
, ErrorC exc
]
runSafeError :: forall e m a p
. ( Carrier m
, Threaders '[ErrorThreads] m p
)
=> SafeErrorC e m a
-> m a
runSafeError =
fmap (fromRight bombPure)
.# runError
.# safeErrorToError
.# introUnder
.# runComposition
{-# INLINE runSafeError #-}
bombPure :: a
bombPure = errorWithoutStackTrace
"runSafeError: Escaped exception! Unless you've imported some internal \
\modules and did something REALLY stupid, this is a bug. Make an issue about \
\it on the GitHub repository for in-other-words."
bombIO :: String -> a
bombIO str = errorWithoutStackTrace $
str ++ ": Escaped exception! This is likely because an `async`ed exceptional \
\computation escaped a `catching` through an `Async`. See \
\Control.Effect.Exceptional.Exceptional. If that sounds unlikely, and you \
\didn't import any internal modules and do something really stupid, \
\then this could be a bug. If so, make an issue about \
\it on the GitHub repository for in-other-words."
type SafeErrorToIOC' s s' exc = CompositionC
'[ IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
, SafeErrorToErrorC exc
, ErrorToIOC' s s' exc
]
type SafeErrorToIOC e m a =
forall s s'
. ReifiesErrorHandler s s' e (ErrorIOToIOC m)
=> SafeErrorToIOC' s s' e m a
safeErrorToIO :: forall e m a
. ( Eff (Embed IO) m
, MonadCatch m
)
=> SafeErrorToIOC e m a
-> m a
safeErrorToIO m =
fmap (fromRight (bombIO "safeErrorToIO"))
$ errorToIO
$ safeErrorToError
$ introUnder
$ runComposition
$ m
{-# INLINE safeErrorToIO #-}
type SafeErrorToErrorIOC' s s' exc = CompositionC
'[ IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
, SafeErrorToErrorC exc
, InterpretErrorC' s s' exc
]
type SafeErrorToErrorIOC e m a =
forall s s'
. ReifiesErrorHandler s s' e m
=> SafeErrorToErrorIOC' s s' e m a
safeErrorToErrorIO :: forall e m a
. Effs '[Embed IO, ErrorIO] m
=> SafeErrorToErrorIOC e m a
-> m a
safeErrorToErrorIO m =
fmap (fromRight (bombIO "safeErrorToErrorIO"))
$ errorToErrorIO
$ safeErrorToError
$ introUnder
$ runComposition
$ m
{-# INLINE safeErrorToErrorIO #-}
type SafeErrorToIOSimpleC exc = CompositionC
'[ IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
, SafeErrorToErrorC exc
, ErrorToIOSimpleC exc
]
safeErrorToIOSimple :: forall e m a p
. ( Eff (Embed IO) m
, MonadCatch m
, Threaders '[ReaderThreads] m p
)
=> SafeErrorToIOSimpleC e m a
-> m a
safeErrorToIOSimple =
fmap (fromRight (bombIO "safeErrorToIOSimple"))
. errorToIOSimple
.# safeErrorToError
.# introUnder
.# runComposition
{-# INLINE safeErrorToIOSimple #-}
type SafeErrorToErrorIOSimpleC exc = CompositionC
'[ IntroUnderC (SafeError exc) '[Catch exc, Throw exc]
, SafeErrorToErrorC exc
, InterpretErrorSimpleC exc
]
safeErrorToErrorIOSimple :: forall e m a p
. ( Effs '[ErrorIO, Embed IO] m
, Threaders '[ReaderThreads] m p
)
=> SafeErrorToErrorIOSimpleC e m a
-> m a
safeErrorToErrorIOSimple =
fmap (fromRight (bombIO "safeErrorToErrorIOSimple"))
. errorToErrorIOSimple
.# safeErrorToError
.# introUnder
.# runComposition
{-# INLINE safeErrorToErrorIOSimple #-}