{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE Trustworthy         #-}
-- |
-- Copyright: (c) 2021 Xy Ren
-- License: BSD3
-- Maintainer: xy.r@outlook.com
-- Stability: experimental
-- Portability: non-portable (GHC only)
module Cleff.Error
  ( -- * Effect
    Error (..)
    -- * Operations
  , throwError
  , catchError
    -- ** Other ways of throwing errors
  , fromEither
  , fromException
  , fromExceptionVia
  , fromExceptionEff
  , fromExceptionEffVia
  , note
    -- ** Other ways of handling errors
  , catchErrorJust
  , catchErrorIf
  , handleError
  , handleErrorJust
  , handleErrorIf
  , tryError
  , tryErrorJust
    -- * Interpretations
  , runError
  , mapError
  ) where

import           Cleff
import           Cleff.Internal.Base
import           Control.Exception    (Exception)
import qualified Control.Exception    as Exc
import           Control.Monad        ((<=<))
import           Data.Any             (Any, pattern Any)
import           Data.Atomics.Counter (AtomicCounter, incrCounter, newCounter)
import           Data.Bool            (bool)
import           System.IO.Unsafe     (unsafePerformIO)

-- * Effect

-- | An effect capable of breaking out of current control flow by throwing an error of type @e@, and handling the
-- errors thrown from computations. This effect roughly corresponds to the @MonadError@ typeclass and @ExceptT@ monad
-- transformer in @mtl@.
data Error e :: Effect where
  ThrowError :: e -> Error e m a
  CatchError :: m a -> (e -> m a) -> Error e m a

-- * Operations

makeEffect_ ''Error

-- | Throw an error in the current computation.
throwError :: Error e :> es => e -> Eff es a

-- | Handle an error if one is thrown from a computation, and then return to normal control flow.
catchError :: Error e :> es
  => Eff es a -- ^ The computation that may throw errors
  -> (e -> Eff es a) -- ^ The handler that is called when an error is thrown
  -> Eff es a

-- | Lift an 'Either' value into the 'Error' effect.
fromEither :: Error e :> es => Either e a -> Eff es a
fromEither :: Either e a -> Eff es a
fromEither = (e -> Eff es a) -> (a -> Eff es a) -> Either e a -> Eff es a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either e -> Eff es a
forall e (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
throwError a -> Eff es a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure

-- | Lift exceptions generated by an 'IO' computation into the 'Error' effect.
fromException ::  e es a. (Exception e, '[Error e, IOE] :>> es) => IO a -> Eff es a
fromException :: IO a -> Eff es a
fromException IO a
m = ((forall a. Eff es a -> IO a) -> IO a) -> Eff es a
forall (m :: Type -> Type) b.
MonadUnliftIO m =>
((forall a. m a -> IO a) -> IO b) -> m b
withRunInIO \forall a. Eff es a -> IO a
run -> IO a -> (e -> IO a) -> IO a
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
Exc.catch IO a
m (Eff es a -> IO a
forall a. Eff es a -> IO a
run (Eff es a -> IO a) -> (e -> Eff es a) -> e -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
forall e (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
throwError @e)

-- | Like 'fromException', but allows to transform the exception into another error type.
fromExceptionVia :: (Exception ex, '[Error er, IOE] :>> es) => (ex -> er) -> IO a -> Eff es a
fromExceptionVia :: (ex -> er) -> IO a -> Eff es a
fromExceptionVia ex -> er
f IO a
m = ((forall a. Eff es a -> IO a) -> IO a) -> Eff es a
forall (m :: Type -> Type) b.
MonadUnliftIO m =>
((forall a. m a -> IO a) -> IO b) -> m b
withRunInIO \forall a. Eff es a -> IO a
run -> IO a -> (ex -> IO a) -> IO a
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
Exc.catch IO a
m (Eff es a -> IO a
forall a. Eff es a -> IO a
run (Eff es a -> IO a) -> (ex -> Eff es a) -> ex -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. er -> Eff es a
forall e (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
throwError (er -> Eff es a) -> (ex -> er) -> ex -> Eff es a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ex -> er
f)

-- | Lift exceptions generated by an 'Eff' computation into the 'Error' effect.
fromExceptionEff ::  e es a. (Exception e, '[Error e, IOE] :>> es) => Eff es a -> Eff es a
fromExceptionEff :: Eff es a -> Eff es a
fromExceptionEff Eff es a
m = ((forall a. Eff es a -> IO a) -> IO a) -> Eff es a
forall (m :: Type -> Type) b.
MonadUnliftIO m =>
((forall a. m a -> IO a) -> IO b) -> m b
withRunInIO \forall a. Eff es a -> IO a
run -> IO a -> (e -> IO a) -> IO a
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
Exc.catch (Eff es a -> IO a
forall a. Eff es a -> IO a
run Eff es a
m) (Eff es a -> IO a
forall a. Eff es a -> IO a
run (Eff es a -> IO a) -> (e -> Eff es a) -> e -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
forall e (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
throwError @e)

-- | Like 'fromExceptionEff', but allows to transform the exception into another error type.
fromExceptionEffVia :: (Exception ex, '[Error er, IOE] :>> es) => (ex -> er) -> Eff es a -> Eff es a
fromExceptionEffVia :: (ex -> er) -> Eff es a -> Eff es a
fromExceptionEffVia ex -> er
f Eff es a
m = ((forall a. Eff es a -> IO a) -> IO a) -> Eff es a
forall (m :: Type -> Type) b.
MonadUnliftIO m =>
((forall a. m a -> IO a) -> IO b) -> m b
withRunInIO \forall a. Eff es a -> IO a
run -> IO a -> (ex -> IO a) -> IO a
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
Exc.catch (Eff es a -> IO a
forall a. Eff es a -> IO a
run Eff es a
m) (Eff es a -> IO a
forall a. Eff es a -> IO a
run (Eff es a -> IO a) -> (ex -> Eff es a) -> ex -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. er -> Eff es a
forall e (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
throwError (er -> Eff es a) -> (ex -> er) -> ex -> Eff es a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ex -> er
f)

-- | Try to extract a value from 'Maybe', throw an error otherwise.
note :: Error e :> es => e -> Maybe a -> Eff es a
note :: e -> Maybe a -> Eff es a
note e
e = Eff es a -> (a -> Eff es a) -> Maybe a -> Eff es a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (e -> Eff es a
forall e (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
throwError e
e) a -> Eff es a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure

-- | A variant of 'catchError' that allows a predicate to choose whether to catch ('Just') or rethrow ('Nothing') the
-- error.
catchErrorJust :: Error e :> es => (e -> Maybe b) -> Eff es a -> (b -> Eff es a) -> Eff es a
catchErrorJust :: (e -> Maybe b) -> Eff es a -> (b -> Eff es a) -> Eff es a
catchErrorJust e -> Maybe b
f Eff es a
m b -> Eff es a
h = Eff es a
m Eff es a -> (e -> Eff es a) -> Eff es a
forall e (es :: [Effect]) a.
(Error e :> es) =>
Eff es a -> (e -> Eff es a) -> Eff es a
`catchError` \e
e -> Eff es a -> (b -> Eff es a) -> Maybe b -> Eff es a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (e -> Eff es a
forall e (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
throwError e
e) b -> Eff es a
h (Maybe b -> Eff es a) -> Maybe b -> Eff es a
forall a b. (a -> b) -> a -> b
$ e -> Maybe b
f e
e

-- | A variant of 'catchError' that allows a predicate to choose whether to catch ('True') or rethrow ('False') the
-- error.
catchErrorIf :: Error e :> es => (e -> Bool) -> Eff es a -> (e -> Eff es a) -> Eff es a
catchErrorIf :: (e -> Bool) -> Eff es a -> (e -> Eff es a) -> Eff es a
catchErrorIf e -> Bool
f Eff es a
m e -> Eff es a
h = Eff es a
m Eff es a -> (e -> Eff es a) -> Eff es a
forall e (es :: [Effect]) a.
(Error e :> es) =>
Eff es a -> (e -> Eff es a) -> Eff es a
`catchError` \e
e -> Eff es a -> Eff es a -> Bool -> Eff es a
forall a. a -> a -> Bool -> a
bool (e -> Eff es a
forall e (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
throwError e
e) (e -> Eff es a
h e
e) (Bool -> Eff es a) -> Bool -> Eff es a
forall a b. (a -> b) -> a -> b
$ e -> Bool
f e
e

-- | Flipped version of 'catchError'.
handleError :: Error e :> es => (e -> Eff es a) -> Eff es a -> Eff es a
handleError :: (e -> Eff es a) -> Eff es a -> Eff es a
handleError = (Eff es a -> (e -> Eff es a) -> Eff es a)
-> (e -> Eff es a) -> Eff es a -> Eff es a
forall a b c. (a -> b -> c) -> b -> a -> c
flip Eff es a -> (e -> Eff es a) -> Eff es a
forall e (es :: [Effect]) a.
(Error e :> es) =>
Eff es a -> (e -> Eff es a) -> Eff es a
catchError

-- | Flipped version of 'catchErrorJust'.
handleErrorJust :: Error e :> es => (e -> Maybe b) -> (b -> Eff es a) -> Eff es a -> Eff es a
handleErrorJust :: (e -> Maybe b) -> (b -> Eff es a) -> Eff es a -> Eff es a
handleErrorJust = (Eff es a -> (b -> Eff es a) -> Eff es a)
-> (b -> Eff es a) -> Eff es a -> Eff es a
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((Eff es a -> (b -> Eff es a) -> Eff es a)
 -> (b -> Eff es a) -> Eff es a -> Eff es a)
-> ((e -> Maybe b) -> Eff es a -> (b -> Eff es a) -> Eff es a)
-> (e -> Maybe b)
-> (b -> Eff es a)
-> Eff es a
-> Eff es a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (e -> Maybe b) -> Eff es a -> (b -> Eff es a) -> Eff es a
forall e (es :: [Effect]) b a.
(Error e :> es) =>
(e -> Maybe b) -> Eff es a -> (b -> Eff es a) -> Eff es a
catchErrorJust

-- | Flipped version of 'catchErrorIf'.
handleErrorIf :: Error e :> es => (e -> Bool) -> (e -> Eff es a) -> Eff es a -> Eff es a
handleErrorIf :: (e -> Bool) -> (e -> Eff es a) -> Eff es a -> Eff es a
handleErrorIf = (Eff es a -> (e -> Eff es a) -> Eff es a)
-> (e -> Eff es a) -> Eff es a -> Eff es a
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((Eff es a -> (e -> Eff es a) -> Eff es a)
 -> (e -> Eff es a) -> Eff es a -> Eff es a)
-> ((e -> Bool) -> Eff es a -> (e -> Eff es a) -> Eff es a)
-> (e -> Bool)
-> (e -> Eff es a)
-> Eff es a
-> Eff es a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (e -> Bool) -> Eff es a -> (e -> Eff es a) -> Eff es a
forall e (es :: [Effect]) a.
(Error e :> es) =>
(e -> Bool) -> Eff es a -> (e -> Eff es a) -> Eff es a
catchErrorIf

-- | Runs a computation, returning a 'Left' value if an error was thrown.
tryError :: Error e :> es => Eff es a -> Eff es (Either e a)
tryError :: Eff es a -> Eff es (Either e a)
tryError Eff es a
m = (a -> Either e a
forall a b. b -> Either a b
Right (a -> Either e a) -> Eff es a -> Eff es (Either e a)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> Eff es a
m) Eff es (Either e a)
-> (e -> Eff es (Either e a)) -> Eff es (Either e a)
forall e (es :: [Effect]) a.
(Error e :> es) =>
Eff es a -> (e -> Eff es a) -> Eff es a
`catchError` (Either e a -> Eff es (Either e a)
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (Either e a -> Eff es (Either e a))
-> (e -> Either e a) -> e -> Eff es (Either e a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> Either e a
forall a b. a -> Either a b
Left)

-- | A variant of 'tryError' that allows a predicate to choose whether to catch ('True') or rethrow ('False') the
-- error.
tryErrorJust :: Error e :> es => (e -> Maybe b) -> Eff es a -> Eff es (Either b a)
tryErrorJust :: (e -> Maybe b) -> Eff es a -> Eff es (Either b a)
tryErrorJust e -> Maybe b
f Eff es a
m = (a -> Either b a
forall a b. b -> Either a b
Right (a -> Either b a) -> Eff es a -> Eff es (Either b a)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> Eff es a
m) Eff es (Either b a)
-> (e -> Eff es (Either b a)) -> Eff es (Either b a)
forall e (es :: [Effect]) a.
(Error e :> es) =>
Eff es a -> (e -> Eff es a) -> Eff es a
`catchError` \e
e -> Eff es (Either b a)
-> (b -> Eff es (Either b a)) -> Maybe b -> Eff es (Either b a)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (e -> Eff es (Either b a)
forall e (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
throwError e
e) (Either b a -> Eff es (Either b a)
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (Either b a -> Eff es (Either b a))
-> (b -> Either b a) -> b -> Eff es (Either b a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. b -> Either b a
forall a b. a -> Either a b
Left) (Maybe b -> Eff es (Either b a)) -> Maybe b -> Eff es (Either b a)
forall a b. (a -> b) -> a -> b
$ e -> Maybe b
f e
e

-- * Interpretations

type ExcUid = Int

-- | Exception wrapper used in 'runError' in order not to conflate error types with exception types.
data ErrorExc = ErrorExc !ExcUid Any
  deriving anyclass (Show ErrorExc
Typeable ErrorExc
Typeable ErrorExc
-> Show ErrorExc
-> (ErrorExc -> SomeException)
-> (SomeException -> Maybe ErrorExc)
-> (ErrorExc -> String)
-> Exception ErrorExc
SomeException -> Maybe ErrorExc
ErrorExc -> String
ErrorExc -> SomeException
forall e.
Typeable e
-> Show e
-> (e -> SomeException)
-> (SomeException -> Maybe e)
-> (e -> String)
-> Exception e
displayException :: ErrorExc -> String
$cdisplayException :: ErrorExc -> String
fromException :: SomeException -> Maybe ErrorExc
$cfromException :: SomeException -> Maybe ErrorExc
toException :: ErrorExc -> SomeException
$ctoException :: ErrorExc -> SomeException
$cp2Exception :: Show ErrorExc
$cp1Exception :: Typeable ErrorExc
Exception)

instance Show ErrorExc where
  showsPrec :: Int -> ErrorExc -> ShowS
showsPrec Int
_ (ErrorExc Int
uid Any
_) =
    (String
"Cleff.Error.runError: Escaped error (error UID: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<>) ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> ShowS
forall a. Show a => a -> ShowS
shows Int
uid ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String
"). This is possibly due \
    \to trying to 'throwError' in a forked thread, or trying to 'wait' on an error-throwing 'Async' computation out \
    \of the effect scope where it is created. Refer to the haddock of 'runError' for details on the caveats. If all \
    \those shenanigans mentioned or other similar ones seem unlikely, please report this as a bug." String -> ShowS
forall a. Semigroup a => a -> a -> a
<>)

catch' ::  e es a. IOE :> es => ExcUid -> Eff es a -> (e -> Eff es a) -> Eff es a
catch' :: Int -> Eff es a -> (e -> Eff es a) -> Eff es a
catch' Int
eid Eff es a
m e -> Eff es a
h = ((forall a. Eff es a -> IO a) -> IO a) -> Eff es a
forall (m :: Type -> Type) b.
MonadUnliftIO m =>
((forall a. m a -> IO a) -> IO b) -> m b
withRunInIO \forall a. Eff es a -> IO a
run -> Eff es a -> IO a
forall a. Eff es a -> IO a
run Eff es a
m IO a -> (ErrorExc -> IO a) -> IO a
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`Exc.catch` \ex :: ErrorExc
ex@(ErrorExc Int
eid' (Any e
e)) ->
  if Int
eid Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
eid' then Eff es a -> IO a
forall a. Eff es a -> IO a
run (Eff es a -> IO a) -> Eff es a -> IO a
forall a b. (a -> b) -> a -> b
$ e -> Eff es a
h e
e else ErrorExc -> IO a
forall e a. Exception e => e -> IO a
Exc.throwIO ErrorExc
ex

try' ::  e es a. IOE :> es => ExcUid -> Eff es a -> Eff es (Either e a)
try' :: Int -> Eff es a -> Eff es (Either e a)
try' Int
eid Eff es a
m = Int
-> Eff es (Either e a)
-> (e -> Eff es (Either e a))
-> Eff es (Either e a)
forall e (es :: [Effect]) a.
(IOE :> es) =>
Int -> Eff es a -> (e -> Eff es a) -> Eff es a
catch' Int
eid (a -> Either e a
forall a b. b -> Either a b
Right (a -> Either e a) -> Eff es a -> Eff es (Either e a)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> Eff es a
m) (Either e a -> Eff es (Either e a)
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (Either e a -> Eff es (Either e a))
-> (e -> Either e a) -> e -> Eff es (Either e a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> Either e a
forall a b. a -> Either a b
Left)

excUidSource :: AtomicCounter
excUidSource :: AtomicCounter
excUidSource = IO AtomicCounter -> AtomicCounter
forall a. IO a -> a
unsafePerformIO (Int -> IO AtomicCounter
newCounter Int
0)
{-# NOINLINE excUidSource #-}

newExcUid :: IO ExcUid
newExcUid :: IO Int
newExcUid = Int -> AtomicCounter -> IO Int
incrCounter Int
1 AtomicCounter
excUidSource

errorHandler :: ExcUid -> Handler (Error e) (IOE : es)
errorHandler :: Int -> Handler (Error e) (IOE : es)
errorHandler Int
eid = \case
  ThrowError e
e     -> IO a -> Eff (IOE : es) a
forall (m :: Type -> Type) a. MonadIO m => IO a -> m a
liftIO (IO a -> Eff (IOE : es) a) -> IO a -> Eff (IOE : es) a
forall a b. (a -> b) -> a -> b
$ ErrorExc -> IO a
forall e a. Exception e => e -> IO a
Exc.throwIO (ErrorExc -> IO a) -> ErrorExc -> IO a
forall a b. (a -> b) -> a -> b
$ Int -> Any -> ErrorExc
ErrorExc Int
eid (e -> Any
forall a. a -> Any
Any e
e)
  CatchError Eff esSend a
m' e -> Eff esSend a
h' -> Int
-> Eff (IOE : es) a -> (e -> Eff (IOE : es) a) -> Eff (IOE : es) a
forall e (es :: [Effect]) a.
(IOE :> es) =>
Int -> Eff es a -> (e -> Eff es a) -> Eff es a
catch' Int
eid (Eff esSend a -> Eff (IOE : es) a
forall (esSend :: [Effect]) (e :: Effect) (es :: [Effect]).
Handling esSend e es =>
Eff esSend ~> Eff es
toEff Eff esSend a
m') (Eff esSend a -> Eff (IOE : es) a
forall (esSend :: [Effect]) (e :: Effect) (es :: [Effect]).
Handling esSend e es =>
Eff esSend ~> Eff es
toEff (Eff esSend a -> Eff (IOE : es) a)
-> (e -> Eff esSend a) -> e -> Eff (IOE : es) a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> Eff esSend a
h')

-- | Run an 'Error' effect.
--
-- === Caveats
--
-- 'runError' is implemented with 'Exc.Exception's therefore inherits some of its unexpected behaviors.
-- Errors thrown in forked threads will /not/ be directly caught by 'catchError's in the parent thread. Instead it will
-- incur an exception, and we won't be quite able to display the details of that exception properly at that point.
-- Therefore please properly handle the errors in the forked threads separately.
--
-- However if you use @async@ and @wait@ for the action in the same effect scope (/i.e./ they get to be interpreted by
-- the same 'runError' handler), the error /will/ be caught in the parent thread even if you don't deal with it in the
-- forked thread. But if you passed the @Async@ value out of the effect scope and @wait@ed for it elsewhere, the error
-- will again not be caught. The best choice is /not to pass @Async@ values around randomly/.
runError ::  e es a. Eff (Error e : es) a -> Eff es (Either e a)
runError :: Eff (Error e : es) a -> Eff es (Either e a)
runError Eff (Error e : es) a
m = Eff (IOE : es) (Either e a) -> Eff es (Either e a)
forall (es :: [Effect]). Eff (IOE : es) ~> Eff es
thisIsPureTrustMe do
  Int
eid <- IO Int -> Eff (IOE : es) Int
forall (m :: Type -> Type) a. MonadIO m => IO a -> m a
liftIO IO Int
newExcUid
  Int -> Eff (IOE : es) a -> Eff (IOE : es) (Either e a)
forall e (es :: [Effect]) a.
(IOE :> es) =>
Int -> Eff es a -> Eff es (Either e a)
try' Int
eid (Handler (Error e) (IOE : es)
-> Eff (Error e : es) a -> Eff (IOE : es) a
forall (e' :: Effect) (e :: Effect) (es :: [Effect]).
Handler e (e' : es) -> Eff (e : es) ~> Eff (e' : es)
reinterpret (Int -> Handler (Error e) (IOE : es)
forall e (es :: [Effect]). Int -> Handler (Error e) (IOE : es)
errorHandler Int
eid) Eff (Error e : es) a
m)

-- | Transform an 'Error' into another. This is useful for aggregating multiple errors into one type.
mapError ::  e e' es. Error e' :> es => (e -> e') -> Eff (Error e : es) ~> Eff es
mapError :: (e -> e') -> Eff (Error e : es) ~> Eff es
mapError e -> e'
f = (e -> Eff es a) -> (a -> Eff es a) -> Either e a -> Eff es a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (e' -> Eff es a
forall e (es :: [Effect]) a. (Error e :> es) => e -> Eff es a
throwError (e' -> Eff es a) -> (e -> e') -> e -> Eff es a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> e'
f) a -> Eff es a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (Either e a -> Eff es a)
-> (Eff (Error e : es) a -> Eff es (Either e a))
-> Eff (Error e : es) a
-> Eff es a
forall (m :: Type -> Type) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< Eff (Error e : es) a -> Eff es (Either e a)
forall e (es :: [Effect]) a.
Eff (Error e : es) a -> Eff es (Either e a)
runError