module Effectful.Error.Static
(
Error
, runError
, runErrorWith
, runErrorNoCallStack
, runErrorNoCallStackWith
, throwErrorWith
, throwError
, throwError_
, catchError
, handleError
, tryError
, HasCallStack
, CallStack
, getCallStack
, prettyCallStack
) where
import Control.Exception
import Data.Kind
import GHC.Stack
import Effectful
import Effectful.Dispatch.Static
import Effectful.Dispatch.Static.Primitive
import Effectful.Internal.Utils
data Error (e :: Type) :: Effect
type instance DispatchOf (Error e) = Static NoSideEffects
newtype instance StaticRep (Error e) = Error ErrorId
runError
:: forall e es a
. HasCallStack
=> Eff (Error e : es) a
-> Eff es (Either (CallStack, e) a)
runError :: forall e (es :: [(Type -> Type) -> Type -> Type]) a.
HasCallStack =>
Eff (Error e : es) a -> Eff es (Either (CallStack, e) a)
runError Eff (Error e : es) a
m = (Env es -> IO (Either (CallStack, e) a))
-> Eff es (Either (CallStack, e) a)
forall (es :: [(Type -> Type) -> Type -> Type]) a.
(Env es -> IO a) -> Eff es a
unsafeEff ((Env es -> IO (Either (CallStack, e) a))
-> Eff es (Either (CallStack, e) a))
-> (Env es -> IO (Either (CallStack, e) a))
-> Eff es (Either (CallStack, e) a)
forall a b. (a -> b) -> a -> b
$ \Env es
es0 -> ((forall a. IO a -> IO a) -> IO (Either (CallStack, e) a))
-> IO (Either (CallStack, e) a)
forall b. ((forall a. IO a -> IO a) -> IO b) -> IO b
mask (((forall a. IO a -> IO a) -> IO (Either (CallStack, e) a))
-> IO (Either (CallStack, e) a))
-> ((forall a. IO a -> IO a) -> IO (Either (CallStack, e) a))
-> IO (Either (CallStack, e) a)
forall a b. (a -> b) -> a -> b
$ \forall a. IO a -> IO a
unmask -> do
ErrorId
eid <- IO ErrorId
newErrorId
Env (Error e : es)
es <- EffectRep (DispatchOf (Error e)) (Error e)
-> Relinker (EffectRep (DispatchOf (Error e))) (Error e)
-> Env es
-> IO (Env (Error e : es))
forall (e :: (Type -> Type) -> Type -> Type)
(es :: [(Type -> Type) -> Type -> Type]).
HasCallStack =>
EffectRep (DispatchOf e) e
-> Relinker (EffectRep (DispatchOf e)) e
-> Env es
-> IO (Env (e : es))
consEnv (forall e. ErrorId -> StaticRep (Error e)
Error @e ErrorId
eid) Relinker (EffectRep (DispatchOf (Error e))) (Error e)
Relinker StaticRep (Error e)
forall (rep :: ((Type -> Type) -> Type -> Type) -> Type)
(e :: (Type -> Type) -> Type -> Type).
Relinker rep e
dummyRelinker Env es
es0
Either (CallStack, e) a
r <- (IO a -> IO a)
-> ErrorId -> Env (Error e : es) -> IO (Either (CallStack, e) a)
tryErrorIO IO a -> IO a
forall a. IO a -> IO a
unmask ErrorId
eid Env (Error e : es)
es IO (Either (CallStack, e) a)
-> IO () -> IO (Either (CallStack, e) a)
forall a b. IO a -> IO b -> IO a
`onException` Env (Error e : es) -> IO ()
forall (e :: (Type -> Type) -> Type -> Type)
(es :: [(Type -> Type) -> Type -> Type]).
HasCallStack =>
Env (e : es) -> IO ()
unconsEnv Env (Error e : es)
es
Env (Error e : es) -> IO ()
forall (e :: (Type -> Type) -> Type -> Type)
(es :: [(Type -> Type) -> Type -> Type]).
HasCallStack =>
Env (e : es) -> IO ()
unconsEnv Env (Error e : es)
es
Either (CallStack, e) a -> IO (Either (CallStack, e) a)
forall a. a -> IO a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure Either (CallStack, e) a
r
where
tryErrorIO :: (IO a -> IO a)
-> ErrorId -> Env (Error e : es) -> IO (Either (CallStack, e) a)
tryErrorIO IO a -> IO a
unmask ErrorId
eid Env (Error e : es)
es = IO a -> IO (Either SomeException a)
forall e a. Exception e => IO a -> IO (Either e a)
try (IO a -> IO a
unmask (IO a -> IO a) -> IO a -> IO a
forall a b. (a -> b) -> a -> b
$ Eff (Error e : es) a -> Env (Error e : es) -> IO a
forall (es :: [(Type -> Type) -> Type -> Type]) a.
Eff es a -> Env es -> IO a
unEff Eff (Error e : es) a
m Env (Error e : es)
es) IO (Either SomeException a)
-> (Either SomeException a -> IO (Either (CallStack, e) a))
-> IO (Either (CallStack, e) a)
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: Type -> Type) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Right a
a -> Either (CallStack, e) a -> IO (Either (CallStack, e) a)
forall a. a -> IO a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (Either (CallStack, e) a -> IO (Either (CallStack, e) a))
-> Either (CallStack, e) a -> IO (Either (CallStack, e) a)
forall a b. (a -> b) -> a -> b
$ a -> Either (CallStack, e) a
forall a b. b -> Either a b
Right a
a
Left SomeException
ex -> SomeException
-> ErrorId
-> (CallStack -> e -> Either (CallStack, e) a)
-> IO (Either (CallStack, e) a)
-> IO (Either (CallStack, e) a)
forall e r.
SomeException -> ErrorId -> (CallStack -> e -> r) -> IO r -> IO r
tryHandler SomeException
ex ErrorId
eid (\CallStack
cs e
e -> (CallStack, e) -> Either (CallStack, e) a
forall a b. a -> Either a b
Left (CallStack
cs, e
e))
(IO (Either (CallStack, e) a) -> IO (Either (CallStack, e) a))
-> IO (Either (CallStack, e) a) -> IO (Either (CallStack, e) a)
forall a b. (a -> b) -> a -> b
$ SomeException -> IO (Either (CallStack, e) a)
forall e a. Exception e => e -> IO a
throwIO SomeException
ex
runErrorWith
:: HasCallStack
=> (CallStack -> e -> Eff es a)
-> Eff (Error e : es) a
-> Eff es a
runErrorWith :: forall e (es :: [(Type -> Type) -> Type -> Type]) a.
HasCallStack =>
(CallStack -> e -> Eff es a) -> Eff (Error e : es) a -> Eff es a
runErrorWith CallStack -> e -> Eff es a
handler Eff (Error e : es) a
m = Eff (Error e : es) a -> Eff es (Either (CallStack, e) a)
forall e (es :: [(Type -> Type) -> Type -> Type]) a.
HasCallStack =>
Eff (Error e : es) a -> Eff es (Either (CallStack, e) a)
runError Eff (Error e : es) a
m Eff es (Either (CallStack, e) a)
-> (Either (CallStack, e) a -> Eff es a) -> Eff es a
forall a b. Eff es a -> (a -> Eff es b) -> Eff es b
forall (m :: Type -> Type) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Left (CallStack
cs, e
e) -> CallStack -> e -> Eff es a
handler CallStack
cs e
e
Right a
a -> a -> Eff es a
forall a. a -> Eff es a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure a
a
runErrorNoCallStack
:: forall e es a
. HasCallStack
=> Eff (Error e : es) a
-> Eff es (Either e a)
runErrorNoCallStack :: forall e (es :: [(Type -> Type) -> Type -> Type]) a.
HasCallStack =>
Eff (Error e : es) a -> Eff es (Either e a)
runErrorNoCallStack = (Either (CallStack, e) a -> Either e a)
-> Eff es (Either (CallStack, e) a) -> Eff es (Either e a)
forall a b. (a -> b) -> Eff es a -> Eff es b
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
fmap (((CallStack, e) -> Either e a)
-> (a -> Either e a) -> Either (CallStack, e) a -> Either e a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (e -> Either e a
forall a b. a -> Either a b
Left (e -> Either e a)
-> ((CallStack, e) -> e) -> (CallStack, e) -> Either e a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CallStack, e) -> e
forall a b. (a, b) -> b
snd) a -> Either e a
forall a b. b -> Either a b
Right) (Eff es (Either (CallStack, e) a) -> Eff es (Either e a))
-> (Eff (Error e : es) a -> Eff es (Either (CallStack, e) a))
-> Eff (Error e : es) a
-> Eff es (Either e a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Eff (Error e : es) a -> Eff es (Either (CallStack, e) a)
forall e (es :: [(Type -> Type) -> Type -> Type]) a.
HasCallStack =>
Eff (Error e : es) a -> Eff es (Either (CallStack, e) a)
runError
runErrorNoCallStackWith
:: HasCallStack
=> (e -> Eff es a)
-> Eff (Error e : es) a
-> Eff es a
runErrorNoCallStackWith :: forall e (es :: [(Type -> Type) -> Type -> Type]) a.
HasCallStack =>
(e -> Eff es a) -> Eff (Error e : es) a -> Eff es a
runErrorNoCallStackWith e -> Eff es a
handler Eff (Error e : es) a
m = Eff (Error e : es) a -> Eff es (Either e a)
forall e (es :: [(Type -> Type) -> Type -> Type]) a.
HasCallStack =>
Eff (Error e : es) a -> Eff es (Either e a)
runErrorNoCallStack Eff (Error e : es) a
m Eff es (Either e a) -> (Either e a -> Eff es a) -> Eff es a
forall a b. Eff es a -> (a -> Eff es b) -> Eff es b
forall (m :: Type -> Type) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Left e
e -> e -> Eff es a
handler e
e
Right a
a -> a -> Eff es a
forall a. a -> Eff es a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure a
a
throwErrorWith
:: forall e es a. (HasCallStack, Error e :> es)
=> (e -> String)
-> e
-> Eff es a
throwErrorWith :: forall e (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, Error e :> es) =>
(e -> String) -> e -> Eff es a
throwErrorWith e -> String
display e
e = (Env es -> IO a) -> Eff es a
forall (es :: [(Type -> Type) -> Type -> Type]) a.
(Env es -> IO a) -> Eff es a
unsafeEff ((Env es -> IO a) -> Eff es a) -> (Env es -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \Env es
es -> do
Error ErrorId
eid <- forall (e :: (Type -> Type) -> Type -> Type)
(es :: [(Type -> Type) -> Type -> Type]).
(HasCallStack, e :> es) =>
Env es -> IO (EffectRep (DispatchOf e) e)
getEnv @(Error e) Env es
es
ErrorWrapper -> IO a
forall e a. Exception e => e -> IO a
throwIO (ErrorWrapper -> IO a) -> ErrorWrapper -> IO a
forall a b. (a -> b) -> a -> b
$ ErrorId -> CallStack -> String -> Any -> ErrorWrapper
ErrorWrapper ErrorId
eid CallStack
HasCallStack => CallStack
callStack (e -> String
display e
e) (e -> Any
forall a. a -> Any
toAny e
e)
throwError
:: forall e es a. (HasCallStack, Error e :> es, Show e)
=> e
-> Eff es a
throwError :: forall e (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, Error e :> es, Show e) =>
e -> Eff es a
throwError = (HasCallStack => (e -> String) -> e -> Eff es a)
-> (e -> String) -> e -> Eff es a
forall a. HasCallStack => (HasCallStack => a) -> a
withFrozenCallStack HasCallStack => (e -> String) -> e -> Eff es a
(e -> String) -> e -> Eff es a
forall e (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, Error e :> es) =>
(e -> String) -> e -> Eff es a
throwErrorWith e -> String
forall a. Show a => a -> String
show
throwError_
:: forall e es a. (HasCallStack, Error e :> es)
=> e
-> Eff es a
throwError_ :: forall e (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, Error e :> es) =>
e -> Eff es a
throwError_ = (HasCallStack => (e -> String) -> e -> Eff es a)
-> (e -> String) -> e -> Eff es a
forall a. HasCallStack => (HasCallStack => a) -> a
withFrozenCallStack HasCallStack => (e -> String) -> e -> Eff es a
(e -> String) -> e -> Eff es a
forall e (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, Error e :> es) =>
(e -> String) -> e -> Eff es a
throwErrorWith (String -> e -> String
forall a b. a -> b -> a
const String
"<opaque>")
catchError
:: forall e es a. (HasCallStack, Error e :> es)
=> Eff es a
-> (CallStack -> e -> Eff es a)
-> Eff es a
catchError :: forall e (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, Error e :> es) =>
Eff es a -> (CallStack -> e -> Eff es a) -> Eff es a
catchError Eff es a
m CallStack -> e -> Eff es a
handler = (Env es -> IO a) -> Eff es a
forall (es :: [(Type -> Type) -> Type -> Type]) a.
(Env es -> IO a) -> Eff es a
unsafeEff ((Env es -> IO a) -> Eff es a) -> (Env es -> IO a) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \Env es
es -> do
Error ErrorId
eid <- forall (e :: (Type -> Type) -> Type -> Type)
(es :: [(Type -> Type) -> Type -> Type]).
(HasCallStack, e :> es) =>
Env es -> IO (EffectRep (DispatchOf e) e)
getEnv @(Error e) Env es
es
ErrorId -> IO a -> (CallStack -> e -> IO a) -> IO a
forall a e. ErrorId -> IO a -> (CallStack -> e -> IO a) -> IO a
catchErrorIO ErrorId
eid (Eff es a -> Env es -> IO a
forall (es :: [(Type -> Type) -> Type -> Type]) a.
Eff es a -> Env es -> IO a
unEff Eff es a
m Env es
es) ((CallStack -> e -> IO a) -> IO a)
-> (CallStack -> e -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \CallStack
cs e
e -> do
Eff es a -> Env es -> IO a
forall (es :: [(Type -> Type) -> Type -> Type]) a.
Eff es a -> Env es -> IO a
unEff (CallStack -> e -> Eff es a
handler CallStack
cs e
e) Env es
es
handleError
:: forall e es a. (HasCallStack, Error e :> es)
=> (CallStack -> e -> Eff es a)
-> Eff es a
-> Eff es a
handleError :: forall e (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, Error e :> es) =>
(CallStack -> e -> Eff es a) -> Eff es a -> Eff es a
handleError = (Eff es a -> (CallStack -> e -> Eff es a) -> Eff es a)
-> (CallStack -> e -> Eff es a) -> Eff es a -> Eff es a
forall a b c. (a -> b -> c) -> b -> a -> c
flip Eff es a -> (CallStack -> e -> Eff es a) -> Eff es a
forall e (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, Error e :> es) =>
Eff es a -> (CallStack -> e -> Eff es a) -> Eff es a
catchError
tryError
:: forall e es a. (HasCallStack, Error e :> es)
=> Eff es a
-> Eff es (Either (CallStack, e) a)
tryError :: forall e (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, Error e :> es) =>
Eff es a -> Eff es (Either (CallStack, e) a)
tryError Eff es a
m = (a -> Either (CallStack, e) a
forall a b. b -> Either a b
Right (a -> Either (CallStack, e) a)
-> Eff es a -> Eff es (Either (CallStack, e) a)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> Eff es a
m) Eff es (Either (CallStack, e) a)
-> (CallStack -> e -> Eff es (Either (CallStack, e) a))
-> Eff es (Either (CallStack, e) a)
forall e (es :: [(Type -> Type) -> Type -> Type]) a.
(HasCallStack, Error e :> es) =>
Eff es a -> (CallStack -> e -> Eff es a) -> Eff es a
`catchError` \CallStack
es e
e -> Either (CallStack, e) a -> Eff es (Either (CallStack, e) a)
forall a. a -> Eff es a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (Either (CallStack, e) a -> Eff es (Either (CallStack, e) a))
-> Either (CallStack, e) a -> Eff es (Either (CallStack, e) a)
forall a b. (a -> b) -> a -> b
$ (CallStack, e) -> Either (CallStack, e) a
forall a b. a -> Either a b
Left (CallStack
es, e
e)
newtype ErrorId = ErrorId Unique
deriving newtype ErrorId -> ErrorId -> Bool
(ErrorId -> ErrorId -> Bool)
-> (ErrorId -> ErrorId -> Bool) -> Eq ErrorId
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ErrorId -> ErrorId -> Bool
== :: ErrorId -> ErrorId -> Bool
$c/= :: ErrorId -> ErrorId -> Bool
/= :: ErrorId -> ErrorId -> Bool
Eq
newErrorId :: IO ErrorId
newErrorId :: IO ErrorId
newErrorId = Unique -> ErrorId
ErrorId (Unique -> ErrorId) -> IO Unique -> IO ErrorId
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> IO Unique
newUnique
tryHandler
:: SomeException
-> ErrorId
-> (CallStack -> e -> r)
-> IO r
-> IO r
tryHandler :: forall e r.
SomeException -> ErrorId -> (CallStack -> e -> r) -> IO r -> IO r
tryHandler SomeException
ex ErrorId
eid0 CallStack -> e -> r
handler IO r
next = case SomeException -> Maybe ErrorWrapper
forall e. Exception e => SomeException -> Maybe e
fromException SomeException
ex of
Just (ErrorWrapper ErrorId
eid CallStack
cs String
_ Any
e)
| ErrorId
eid0 ErrorId -> ErrorId -> Bool
forall a. Eq a => a -> a -> Bool
== ErrorId
eid -> r -> IO r
forall a. a -> IO a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (r -> IO r) -> r -> IO r
forall a b. (a -> b) -> a -> b
$ CallStack -> e -> r
handler CallStack
cs (Any -> e
forall a. Any -> a
fromAny Any
e)
| Bool
otherwise -> IO r
next
Maybe ErrorWrapper
Nothing -> IO r
next
data ErrorWrapper = ErrorWrapper !ErrorId CallStack String Any
instance Show ErrorWrapper where
showsPrec :: Int -> ErrorWrapper -> ShowS
showsPrec Int
_ (ErrorWrapper ErrorId
_ CallStack
cs String
errRep Any
_)
= (String
"Effectful.Error.Static.ErrorWrapper: " String -> ShowS
forall a. [a] -> [a] -> [a]
++)
ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String
errRep String -> ShowS
forall a. [a] -> [a] -> [a]
++)
ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String
"\n" String -> ShowS
forall a. [a] -> [a] -> [a]
++)
ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CallStack -> String
prettyCallStack CallStack
cs String -> ShowS
forall a. [a] -> [a] -> [a]
++)
instance Exception ErrorWrapper
catchErrorIO :: ErrorId -> IO a -> (CallStack -> e -> IO a) -> IO a
catchErrorIO :: forall a e. ErrorId -> IO a -> (CallStack -> e -> IO a) -> IO a
catchErrorIO ErrorId
eid IO a
m CallStack -> e -> IO a
handler = do
IO a
m IO a -> (ErrorWrapper -> IO a) -> IO a
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catch` \err :: ErrorWrapper
err@(ErrorWrapper ErrorId
etag CallStack
cs String
_ Any
e) -> do
if ErrorId
eid ErrorId -> ErrorId -> Bool
forall a. Eq a => a -> a -> Bool
== ErrorId
etag
then CallStack -> e -> IO a
handler CallStack
cs (Any -> e
forall a. Any -> a
fromAny Any
e)
else ErrorWrapper -> IO a
forall e a. Exception e => e -> IO a
throwIO ErrorWrapper
err