control-monad-exception-0.4.8: Explicitly typed, checked exceptions with stack traces



A Monad Transformer for explicitly typed checked exceptions.

The exceptions thrown by a computation are inferred by the typechecker and appear in the type signature of the computation as Throws constraints.

Exceptions are defined using the extensible exceptions framework of Marlow (documented in Control.Exception):

  • An Extensible Dynamically-Typed Hierarchy of Exceptions, by Simon Marlow, in Haskell '06.


 data DivideByZero = DivideByZero deriving (Show, Typeable)
 data SumOverflow  = SumOverflow  deriving (Show, Typeable)
 instance Exception DivideByZero
 instance Exception SumOverflow
 data Expr = Add Expr Expr | Div Expr Expr | Val Double
 eval (Val x)     = return x
 eval (Add a1 a2) = do
    v1 <- eval a1
    v2 <- eval a2
    let sum = v1 + v2
    if sum < v1 || sum < v2 then throw SumOverflow else return sum
 eval (Div a1 a2) = do
    v1 <- eval a1
    v2 <- eval a2
    if v2 == 0 then throw DivideByZero else return (v1 / v2)

GHCi infers the following types

 eval                                             :: (Throws DivideByZero l, Throws SumOverflow l) => Expr -> EM l Double
 eval `catch` \ (e::DivideByZero) -> return (-1)  :: Throws SumOverflow l => Expr -> EM l Double
 runEM(eval `catch` \ (e::SomeException) -> return (-1))
                                                  :: Expr -> Double

Notes about type errors and exception hierarchies

  • A type error of the form:
    No instance for (UncaughtException MyException)
      arising from a use of `g' at examples/docatch.hs:21:32-35
    Possible fix:
      add an instance declaration for (UncaughtException MyException)
    In the expression: g ()

is the type checker saying:

"hey, you are trying to run a computation which throws a MyException without handling it, and I won't let you"

Either handle it or declare MyException as an UncaughtException.

  • A type error of the form:
    Overlapping instances for Throws MyException (Caught e NoExceptions)
      arising from a use of `g' at docatch.hs:24:3-6
    Matching instances:
      instance (Throws e l) => Throws e (Caught e' l)
        -- Defined at ../Control/Monad/Exception/Throws.hs:46:9-45
      instance (Exception e) => Throws e (Caught e l)
        -- Defined at ../Control/Monad/Exception/Throws.hs:47:9-44
    (The choice depends on the instantiation of `e'

is due to an exception handler for MyException missing a type annotation to pin down the type of the exception.

  • If your sets of exceptions are hierarchical then you need to teach Throws about the hierarchy.
                                                 --   TopException
                                                 --         |
   instance Throws MidException   TopException   --         |
                                                 --   MidException
   instance Throws ChildException MidException   --         |
   instance Throws ChildException TopException   --         |
                                                 --  ChildException
  • Stack traces are only provided for explicitly annotated program points. For now there is the TH macro withLocTH to help with this. Eventually a preprocessor could be written to automatically insert calls to withLoc at every do statement.
       f () = $withLocTH $ throw MyException
       g a  = $withLocTH $ f a

       main = runEMT $ $withLocTH $ do
       g () `catchWithSrcLoc` \loc (e::MyException) -> lift(putStrLn$ showExceptionWithTrace loc e)
        -- Running main produces the output:
       *Main> main
        in Main(example.hs): (12,6)
           Main(example.hs): (11,7)



type EM l = EMT l IdentitySource

A monad of explicitly typed, checked exceptions

tryEM :: EM (AnyException l) a -> Either SomeException aSource

Run a computation explicitly handling exceptions

runEM :: EM NoExceptions a -> aSource

Run a safe computation

runEMParanoid :: EM ParanoidMode a -> aSource

Run a computation checking even unchecked (UncaughtExceptions) exceptions

data EMT l m a Source


(Monoid w, MonadRWS r w s m) => MonadRWS r w s (EMT l m) 
MonadReader r m => MonadReader r (EMT l m) 
MonadState s m => MonadState s (EMT l m) 
(Monoid w, MonadWriter w m) => MonadWriter w (EMT l m) 
(Exception e, Throws e l, Monad m) => MonadThrow e (EMT l m) 
(Exception e, Monad m) => MonadCatch e (EMT (Caught e l) m) (EMT l m) 
MonadTrans (EMT l) 
Monad m => Monad (EMT l m) 
Monad m => Functor (EMT l m) 
MonadFix m => MonadFix (EMT l m) 
Monad m => Applicative (EMT l m) 
MonadCont m => MonadCont (EMT l m) 
(Throws SomeException l, MonadIO m) => MonadIO (EMT l m) 
Monad m => WithSrcLoc (EMT l m a) 

tryEMT :: Monad m => EMT (AnyException l) m a -> m (Either SomeException a)Source

Run a computation explicitly handling exceptions

runEMT :: Monad m => EMT NoExceptions m a -> m aSource

Run a safe computation

runEMTParanoid :: Monad m => EMT ParanoidMode m a -> m aSource

Run a safe computation checking even unchecked (UncaughtException) exceptions

class WithSrcLoc a whereSource

Generating stack traces for exceptions


withLoc :: String -> a -> aSource

withLoc records the given source location in the exception stack trace when used to wrap a EMT computation.

On any other monad or value, withLoc is defined as the identity


WithSrcLoc a 
Monad m => WithSrcLoc (EMT l m a) 

withLocTH :: Q ExpSource

withLocTH is a convenient TH macro which expands to withLoc <source location> Usage:

 f x = $withLocTH $ do