{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE Safe #-}
module Control.Eff.Exception ( Exc (..)
, exc
, withException
, Fail
, throwError
, throwError_
, die
, runError
, runFail
, catchError
, onFail
, rethrowError
, liftEither
, liftEitherM
, liftMaybe
, liftMaybeM
, ignoreFail
) where
import Control.Eff
import Control.Eff.Extend
import Control.Monad (void)
import Control.Monad.Base
import Control.Monad.Trans.Control
import Data.Function (fix)
newtype Exc e v = Exc e
withException :: Monad m => a -> m (Either e a)
withException = return . Right
exc :: Monad m => e -> m (Either e a)
exc = return . Left
instance Monad m => Handle (Exc e) r a (m (Either e a)) where
handle _ _ (Exc e) = exc e
instance ( MonadBase m m
, LiftedBase m r
) => MonadBaseControl m (Eff (Exc e ': r)) where
type StM (Eff (Exc e ': r)) a = StM (Eff r) (Either e a)
liftBaseWith f = raise $ liftBaseWith $ \runInBase ->
f (runInBase . runError)
restoreM x = do r :: Either e a <- raise (restoreM x)
liftEither r
type Fail = Exc ()
throwError :: (Member (Exc e) r) => e -> Eff r a
throwError e = send (Exc e)
{-# INLINE throwError #-}
throwError_ :: (Member (Exc e) r) => e -> Eff r ()
throwError_ = throwError
{-# INLINE throwError_ #-}
die :: Member Fail r => Eff r a
die = throwError ()
{-# INLINE die #-}
runError :: Eff (Exc e ': r) a -> Eff r (Either e a)
runError = fix (handle_relay withException)
runFail :: Eff (Fail ': r) a -> Eff r (Maybe a)
runFail = fmap (either (const Nothing) Just) . runError
{-# INLINE runFail #-}
catchError :: Member (Exc e) r =>
Eff r a -> (e -> Eff r a) -> Eff r a
catchError m h = fix (respond_relay' (\_ _ (Exc e) -> h e) return) m
onFail :: Eff (Fail ': r) a
-> Eff r a
-> Eff r a
onFail e handle_ = runFail e >>= maybe handle_ return
{-# INLINE onFail #-}
rethrowError :: (Member (Exc e') r)
=> (e -> e')
-> Eff (Exc e ': r) a
-> Eff r a
rethrowError t e = runError e >>= either (throwError . t) return
liftEither :: (Member (Exc e) r) => Either e a -> Eff r a
liftEither = either throwError return
{-# INLINE liftEither #-}
liftEitherM :: (Member (Exc e) r, Lifted m r)
=> m (Either e a)
-> Eff r a
liftEitherM m = lift m >>= liftEither
{-# INLINE liftEitherM #-}
liftMaybe :: Member Fail r => Maybe a -> Eff r a
liftMaybe = maybe die return
{-# INLINE liftMaybe #-}
liftMaybeM :: (Member Fail r, Lifted m r)
=> m (Maybe a)
-> Eff r a
liftMaybeM m = lift m >>= liftMaybe
{-# INLINE liftMaybeM #-}
ignoreFail :: Eff (Fail ': r) a
-> Eff r ()
ignoreFail e = void e `onFail` return ()
{-# INLINE ignoreFail #-}