{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
-- | Add-ons to 'Control.Eff.Exception'
module Control.Eff.ExceptionExtra
( try
, liftRethrow
, runErrorRethrowIO
, liftCatch
, liftCatchEff
, liftTry
, module X
)
where
import Control.Monad ( (>=>) )
import qualified Control.Exception as Exc
import Control.Eff
import Control.Eff.Lift
import Control.Eff.Exception as X
import GHC.Stack
-- * Effects combining "Control.Eff.Exception" and 'Exc.try'
-- | Catch an exception and return it in an 'Either'.
try :: forall e r a . Member (Exc e) r => Eff r a -> Eff r (Either e a)
try e = catchError (Right <$> e) (return . Left)
-- | Lift an IO action and catch all error using 'Exc.try' then wrap the
-- 'Exc.Exception' using a given wrapper function and rethrow it using
-- 'X.throwError'.
liftRethrow
:: forall proxy e r a
. (Exc.Exception e, SetMember Lift (Lift IO) r, SetMember Exc (Exc e) r)
=> proxy e
-> IO a
-> Eff r a
liftRethrow _ m = lift (Exc.try m) >>= either (throwError @e) return
-- | Run an effect with exceptions like 'X.runError' and rethrow it as
-- 'Exc.SomeException' using 'Exc.throw'
runErrorRethrowIO
:: forall e r a
. (Exc.Exception e, SetMember Lift (Lift IO) r)
=> Eff (Exc e ': r) a
-> Eff r a
runErrorRethrowIO = runError >=> either (Exc.throw . Exc.SomeException) return
-- * Effect Utilities around 'Exc.try'
-- | Lift an IO action and catch all errors with 'Exc.try' with a pure
-- handler.
liftCatch
:: forall e r a
. (HasCallStack, Exc.Exception e, SetMember Lift (Lift IO) r)
=> (e -> a)
-> IO a
-> Eff r a
liftCatch handleE m =
lift (Exc.try m) >>= either (return . handleE) return
-- | Lift an IO action and catch all errors with 'Exc.try' with an effect
-- handler.
liftCatchEff
:: forall e r a
. (SetMember Lift (Lift IO) r, HasCallStack, Exc.Exception e)
=> (e -> Eff r a)
-> IO a
-> Eff r a
liftCatchEff handleE m =
lift (Exc.try m) >>= either handleE return
-- | Catch 'Exc.Exception' thrown by an effect.
liftTry
:: forall e r a
. (HasCallStack, Exc.Exception e, SetMember Lift (Lift IO) r)
=> Eff r a
-> Eff r (Either e a)
liftTry m =
(Right <$> m) `catchDynE` (return . Left)