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.
Example
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 towithLoc
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 MyException in Main(example.hs): (12,6) Main(example.hs): (11,7)
- type EM l = EMT l Identity
- tryEM :: EM (AnyException l) a -> Either SomeException a
- runEM :: EM NoExceptions a -> a
- runEMParanoid :: EM ParanoidMode a -> a
- data EMT l m a
- tryEMT :: Monad m => EMT (AnyException l) m a -> m (Either SomeException a)
- runEMT :: Monad m => EMT NoExceptions m a -> m a
- runEMTParanoid :: Monad m => EMT ParanoidMode m a -> m a
- class WithSrcLoc a where
- withLocTH :: Q Exp
- data MonadZeroException = MonadZeroException
- module Control.Monad.Exception.Class
Documentation
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
(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
WithSrcLoc a | |
Monad m => WithSrcLoc (EMT l m a) |
data MonadZeroException Source