packages feed

monadology-0.4: src/Control/Monad/Ology/General/Exception/Class.hs

module Control.Monad.Ology.General.Exception.Class where

import Control.Exception qualified as CE
import Control.Monad.Ology.Specific.Result
import Import

-- | Pretty much every monad can be made an instance of this class.
class Monad m => MonadException m where
    -- | The type of /all/ exceptions of this monad.
    type Exc m :: Type
    throwExc :: Exc m -> m a
    catchExc :: m a -> (Exc m -> m a) -> m a

instance MonadException Identity where
    type Exc Identity = Void
    throwExc = absurd
    catchExc ma _ = ma

instance MonadException ((->) r) where
    type Exc ((->) r) = Void
    throwExc = absurd
    catchExc ma _ = ma

instance Monoid p => MonadException ((,) p) where
    type Exc ((,) p) = Void
    throwExc = absurd
    catchExc ma _ = ma

instance MonadException Maybe where
    type Exc Maybe = ()
    throwExc () = Nothing
    catchExc Nothing handler = handler ()
    catchExc ma _ = ma

instance MonadException [] where
    type Exc [] = ()
    throwExc _ = []
    catchExc [] handler = handler ()
    catchExc ma _ = ma

instance MonadException (Either e) where
    type Exc (Either e) = e
    throwExc = Left
    catchExc (Right a) _ = Right a
    catchExc (Left e) handler = handler e

instance MonadException (Result e) where
    type Exc (Result e) = e
    throwExc = FailureResult
    catchExc (SuccessResult a) _ = SuccessResult a
    catchExc (FailureResult e) handler = handler e

instance MonadException IO where
    type Exc IO = CE.SomeException
    throwExc = CE.throwIO
    catchExc = CE.catch

-- | Catch all exceptions, optionally returning or re-throwing.
catchSomeExc ::
       forall m a. MonadException m
    => m a
    -> (Exc m -> m (Maybe a))
    -> m a
catchSomeExc ma handler = catchExc ma $ \e -> handler e >>= maybe (throwExc e) return

fromResultExc ::
       forall m a. MonadException m
    => Result (Exc m) a
    -> m a
fromResultExc (SuccessResult a) = return a
fromResultExc (FailureResult e) = throwExc e

-- | Catch all exceptions as a 'Result'.
tryExc ::
       forall m a. MonadException m
    => m a
    -> m (Result (Exc m) a)
tryExc ma = catchExc (fmap SuccessResult ma) $ \e -> return $ FailureResult e

-- | Run the handler on exception.
-- Does not mask asynchronous exceptions on the handler.
onException ::
       forall m a. MonadException m
    => m a
    -> m ()
    -> m a
onException ma handler = catchExc ma $ \ex -> handler >> throwExc ex

-- | This catches certain "bottom values".
-- Of course, since non-termination is bottom, this cannot catch all bottoms.
catchPureError :: a -> IO (Maybe CE.SomeException)
catchPureError a = catchExc (CE.evaluate a >> return Nothing) $ \e -> return $ Just e