packages feed

monadology-0.1: src/Control/Monad/Ology/Data/Exn.hs

module Control.Monad.Ology.Data.Exn where

import Control.Monad.Ology.General
import Control.Monad.Ology.Specific.Result
import Import

-- | Exceptions that can be thrown and caught in this monad.
type Exn :: (Type -> Type) -> Type -> Type
data Exn m e = MkExn
    { exnThrow :: forall a. e -> m a
    , exnCatch :: forall a. m a -> (e -> m a) -> m a
    }

instance Invariant (Exn m) where
    invmap f g (MkExn t c) = MkExn (t . g) (\ma ema -> c ma $ ema . f)

instance Summable (Exn m) where
    rVoid = MkExn {exnThrow = absurd, exnCatch = \m _ -> m}
    exn1 <+++> exn2 =
        MkExn
            { exnThrow = either (exnThrow exn1) (exnThrow exn2)
            , exnCatch = \m k -> exnCatch exn1 (exnCatch exn2 m (k . Right)) (k . Left)
            }

exnTry :: Monad m => Exn m e -> m a -> m (Result e a)
exnTry exn ma = exnCatch exn (fmap SuccessResult ma) $ \e -> return $ FailureResult e

exnHandle :: Exn m e -> (e -> m a) -> m a -> m a
exnHandle exn handler ma = exnCatch exn ma handler

exnOnException ::
       forall e m a. Monad m
    => Exn m e
    -> m a
    -> m ()
    -> m a
exnOnException exn ma handler = exnCatch exn ma $ \e -> handler >> exnThrow exn e

exnBracket ::
       forall e m a b. MonadTunnelIO m
    => Exn m e
    -> m a
    -> (a -> m ())
    -> (a -> m b)
    -> m b
exnBracket exn before after thing =
    mask $ \restore -> do
        a <- before
        r <- exnOnException exn (restore (thing a)) (after a)
        _ <- after a
        return r

exnFinally ::
       forall e m a. MonadTunnelIO m
    => Exn m e
    -> m a
    -> m ()
    -> m a
exnFinally exn ma handler = exnBracket exn (return ()) (const handler) (const ma)

exnBracket_ ::
       forall e m. MonadTunnelIO m
    => Exn m e
    -> m ()
    -> m ()
    -> m --> m
exnBracket_ exn before after thing = exnBracket exn before (const after) (const thing)

mapExn :: (e2 -> e1) -> (e1 -> Maybe e2) -> Exn m e1 -> Exn m e2
mapExn f g exn =
    MkExn
        { exnThrow = exnThrow exn . f
        , exnCatch =
              \ma handler ->
                  exnCatch exn ma $ \e ->
                      case g e of
                          Nothing -> exnThrow exn e
                          Just e' -> handler e'
        }

liftExn ::
       forall t m. (MonadTransTunnel t, Monad m)
    => Exn m --> Exn (t m)
liftExn (MkExn t c :: Exn m e) = let
    t' :: forall a. e -> t m a
    t' e = lift $ t e
    c' :: forall a. t m a -> (e -> t m a) -> t m a
    c' tma handler = tunnel $ \unlift -> c (unlift tma) $ \e -> unlift $ handler e
    in MkExn t' c'

allExn ::
       forall m. MonadException m
    => Exn m (Exc m)
allExn = MkExn throwExc catchExc

someExn ::
       forall e m. MonadCatch e m
    => Exn m e
someExn = MkExn throw catch