{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
module Control.Monad.Trans.Except.Extra (
newExceptT
, runExceptT
, exceptT
, left
, right
, mapExceptT
, hoistEither
, bimapExceptT
, firstExceptT
, secondExceptT
, hoistMaybe
, hoistExceptT
, handleIOExceptT
, handleExceptT
, handlesExceptT
, handleLeftT
, catchIOExceptT
, catchExceptT
, catchesExceptT
, catchLeftT
, bracketExceptT
, bracketExceptionT
) where
import Control.Exception (Exception, IOException, SomeException)
import qualified Control.Exception as Exception
import Control.Monad (Monad(..), (=<<))
import Control.Monad.Catch (Handler (..), MonadCatch, MonadMask, catchAll, mask, throwM)
import qualified Control.Monad.Catch as Catch
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Except
import Data.Maybe (Maybe, maybe)
import Data.Either (Either(..), either)
import Data.Foldable (Foldable, foldr)
import Data.Function (($), (.), const, id, flip)
import Data.Functor (Functor(..))
import System.IO (IO)
newExceptT :: m (Either x a) -> ExceptT x m a
newExceptT =
ExceptT
{-# INLINE newExceptT #-}
exceptT :: Monad m => (x -> m b) -> (a -> m b) -> ExceptT x m a -> m b
exceptT f g m =
either f g =<< runExceptT m
{-# INLINE exceptT #-}
left :: Monad m => x -> ExceptT x m a
left =
ExceptT . return . Left
{-# INLINE left #-}
right :: Monad m => a -> ExceptT x m a
right =
return
{-# INLINE right #-}
hoistEither :: Monad m => Either x a -> ExceptT x m a
hoistEither =
ExceptT . return
{-# INLINE hoistEither #-}
bimapExceptT :: Functor m => (x -> y) -> (a -> b) -> ExceptT x m a -> ExceptT y m b
bimapExceptT f g =
let
h (Left e) = Left (f e)
h (Right a) = Right (g a)
in
mapExceptT (fmap h)
{-# INLINE bimapExceptT #-}
firstExceptT :: Functor m => (x -> y) -> ExceptT x m a -> ExceptT y m a
firstExceptT f =
bimapExceptT f id
{-# INLINE firstExceptT #-}
secondExceptT :: Functor m => (a -> b) -> ExceptT x m a -> ExceptT x m b
secondExceptT =
bimapExceptT id
{-# INLINE secondExceptT #-}
hoistMaybe :: Monad m => x -> Maybe a -> ExceptT x m a
hoistMaybe x =
maybe (left x) return
{-# INLINE hoistMaybe #-}
hoistExceptT :: (forall b. m b -> n b) -> ExceptT x m a -> ExceptT x n a
hoistExceptT f =
ExceptT . f . runExceptT
{-# INLINE hoistExceptT #-}
handleIOExceptT :: MonadIO m => (IOException -> x) -> IO a -> ExceptT x m a
handleIOExceptT wrap =
firstExceptT wrap . newExceptT . liftIO . Exception.try
{-# INLINE handleIOExceptT #-}
catchIOExceptT :: MonadIO m => IO a -> (IOException -> x) -> ExceptT x m a
catchIOExceptT = flip handleIOExceptT
{-# INLINE catchIOExceptT #-}
handleExceptT :: (MonadCatch m, Exception e) => (e -> x) -> m a -> ExceptT x m a
handleExceptT wrap =
firstExceptT wrap . newExceptT . Catch.try
{-# INLINE handleExceptT #-}
catchExceptT :: (MonadCatch m, Exception e) => m a -> (e -> x) -> ExceptT x m a
catchExceptT = flip handleExceptT
{-# INLINE catchExceptT #-}
handlesExceptT :: (Foldable f, MonadCatch m) => f (Handler m x) -> m a -> ExceptT x m a
handlesExceptT wrappers action =
newExceptT (fmap Right action `Catch.catch` fmap (fmap Left) handler)
where
handler e =
let probe (Handler h) xs =
maybe xs h (Exception.fromException e)
in
foldr probe (Catch.throwM e) wrappers
catchesExceptT :: (Foldable f, MonadCatch m) => m a -> f (Handler m x) -> ExceptT x m a
catchesExceptT = flip handlesExceptT
{-# INLINE catchesExceptT #-}
handleLeftT :: Monad m => (e -> ExceptT e m a) -> ExceptT e m a -> ExceptT e m a
handleLeftT handler thing = do
r <- lift $ runExceptT thing
case r of
Left e ->
handler e
Right a ->
return a
{-# INLINE handleLeftT #-}
catchLeftT :: Monad m => ExceptT e m a -> (e -> ExceptT e m a) -> ExceptT e m a
catchLeftT = flip handleLeftT
{-# INLINE catchLeftT #-}
bracketExceptT :: Monad m => ExceptT e m a -> (a -> ExceptT e m b) -> (a -> ExceptT e m c) -> ExceptT e m c
bracketExceptT before after thing = do
a <- before
r <- (\err -> after a >> left err) `handleLeftT` thing a
_ <- after a
return r
{-# INLINE bracketExceptT #-}
bracketExceptionT ::
MonadMask m
=> ExceptT e m a
-> (a -> ExceptT e m c)
-> (a -> ExceptT e m b)
-> ExceptT e m b
bracketExceptionT acquire release run =
ExceptT $ bracketF
(runExceptT acquire)
(\r -> case r of
Left _ ->
return . Right $ ()
Right r' ->
runExceptT (release r') >>= \x -> return $ case x of
Left err -> Left (Left err)
Right _ -> Right ())
(\r -> case r of
Left err ->
return . Left $ err
Right r' ->
runExceptT (run r'))
{-# INLINE bracketExceptionT #-}
data BracketResult a =
BracketOk a
| BracketFailedFinalizerOk SomeException
| BracketFailedFinalizerError a
bracketF :: MonadMask m => m a -> (a -> m (Either b c)) -> (a -> m b) -> m b
bracketF a f g =
mask $ \restore -> do
a' <- a
x <- restore (BracketOk `fmap` g a') `catchAll`
(\ex -> either BracketFailedFinalizerError (const $ BracketFailedFinalizerOk ex) `fmap` f a')
case x of
BracketFailedFinalizerOk ex ->
throwM ex
BracketFailedFinalizerError b ->
return b
BracketOk b -> do
z <- f a'
return $ either id (const b) z
{-# INLINE bracketF #-}