| Safe Haskell | Safe |
|---|---|
| Language | Haskell98 |
Control.Monad.Exception.Pure
Contents
Description
A Monad Transformer for explicitly typed checked exceptions, described in detail by:
- Jose Iborra, "Explicitly Typed Exceptions for Haskell", PADL'10, January 2010, http://dl.dropbox.com/s/lgm12trtl0swtra/PADL10.pdf?dl=1
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 -> DoubleSynopsis
- type EM l = EMT l Identity
- tryEM :: EM AnyException a -> Either SomeException a
- tryEMWithLoc :: EM AnyException a -> Either (CallTrace, SomeException) a
- runEM :: EM NoExceptions a -> a
- runEMParanoid :: EM ParanoidMode a -> a
- newtype EMT l m a = EMT {
- unEMT :: m (Either (CallTrace, CheckedException l) a)
- type CallTrace = [String]
- tryEMT :: Monad m => EMT AnyException m a -> m (Either SomeException a)
- tryEMTWithLoc :: Monad m => EMT AnyException m a -> m (Either (CallTrace, SomeException) a)
- runEMT :: Monad m => EMT NoExceptions m a -> m a
- runEMTParanoid :: Monad m => EMT ParanoidMode m a -> m a
- data AnyException
- class Exception e => UncaughtException e
- class Exception e => Throws e l
- data Caught e l
- throw :: (Exception e, Throws e l, Monad m) => e -> EMT l m a
- rethrow :: (Throws e l, Monad m) => CallTrace -> e -> EMT l m a
- catch :: (Exception e, Monad m) => EMT (Caught e l) m a -> (e -> EMT l m a) -> EMT l m a
- catchWithSrcLoc :: (Exception e, Monad m) => EMT (Caught e l) m a -> (CallTrace -> e -> EMT l m a) -> EMT l m a
- finally :: Monad m => EMT l m a -> EMT l m b -> EMT l m a
- onException :: Monad m => EMT l m a -> EMT l m b -> EMT l m a
- bracket :: Monad m => EMT l m a -> (a -> EMT l m b) -> (a -> EMT l m c) -> EMT l m c
- wrapException :: (Exception e, Throws e' l, Monad m) => (e -> e') -> EMT (Caught e l) m a -> EMT l m a
- showExceptionWithTrace :: Exception e => [String] -> e -> String
- data FailException = FailException String
- data MonadZeroException = MonadZeroException
- mplusDefault :: Monad m => EMT l m a -> EMT l m a -> EMT l m a
- class (Typeable e, Show e) => Exception e where
- toException :: e -> SomeException
- fromException :: SomeException -> Maybe e
- displayException :: e -> String
- data SomeException where
- SomeException :: forall e. Exception e => e -> SomeException
- class Monad f => Failure e (f :: Type -> Type) where
- failure :: e -> f v
Important trivia
Hierarchies of Exceptions
If your exceptions are hierarchical then you need to
teach Throws about the hierarchy. See the documentation of
Throws for more info.
Unchecked exceptions
Unchecked exceptions
An exception E can be declared as unchecked by making E an instance of
UncaughtException.
instance UncaughtException E
E will still appear in the list of exceptions thrown
by a computation but runEMT will not complain if E escapes the computation
being run.
Also, tryEMT allows you to run a computation regardless of the exceptions it throws.
Stack Traces
Stack traces are provided via the MonadLoc package. All you need to do is add the following pragma at the top of your Haskell source files and use do-notation:
{ # OPTIONS_GHC -F -pgmF MonadLoc # }Only statements in do blocks appear in the stack trace.
Example:
f () = do throw MyException
g a = do f a
main = runEMT $ do
g () `catchWithSrcLoc`
\loc (e::MyException) -> lift(putStrLn$ showExceptionWithTrace loc e)-- Running main produces the output:
*Main> main
MyException
in f, Main(example.hs): (1,6)
g, Main(example.hs): (2,6)
main, Main(example.hs): (5,9)
main, Main(example.hs): (4,16)Understanding GHC errors
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.
The EM monad
tryEM :: EM AnyException a -> Either SomeException a Source #
Run a computation explicitly handling exceptions
tryEMWithLoc :: EM AnyException a -> Either (CallTrace, SomeException) a Source #
runEM :: EM NoExceptions a -> a Source #
Run a safe computation
runEMParanoid :: EM ParanoidMode a -> a Source #
Run a computation checking even unchecked (UncaughtExceptions) exceptions
The EMT monad transformer
A Monad Transformer for explicitly typed checked exceptions.
Constructors
| EMT | |
Fields
| |
Instances
tryEMT :: Monad m => EMT AnyException m a -> m (Either SomeException a) Source #
Run a computation explicitly handling exceptions
tryEMTWithLoc :: Monad m => EMT AnyException m a -> m (Either (CallTrace, SomeException) a) Source #
runEMTParanoid :: Monad m => EMT ParanoidMode m a -> m a Source #
Run a safe computation checking even unchecked (UncaughtException) exceptions
data AnyException Source #
Instances
| Exception e => Throws e AnyException Source # | |
Defined in Control.Monad.Exception.Base | |
class Exception e => UncaughtException e Source #
UncaughtException models unchecked exceptions
In order to declare an unchecked exception E,
all that is needed is to make e an instance of UncaughtException
instance UncaughtException E
Note that declaring an exception E as unchecked does not automatically turn its children unchecked too. This is a shortcoming of the current encoding.
Instances
| UncaughtException SomeException Source # | |
Defined in Control.Monad.Exception.Base | |
The Throws type class
class Exception e => Throws e l Source #
Throws is a type level binary relationship
used to model a list of exceptions.
There is only one case in which the user must
add further instances to Throws.
If your sets of exceptions are hierarchical then you need to
teach Throws about the hierarchy.
- Subtyping
- As there is no way to automatically infer
the subcases of an exception, they have to be encoded
manually mirroring the hierarchy defined in the defined
Exceptioninstances. For example, the following instance encodes thatMyFileNotFoundExceptionis a subexception ofMyIOException:
instance Throws MyFileNotFoundException (Caught MyIOException l)
Throws is not a transitive relation and every ancestor relation
must be explicitly encoded.
-- TopException
-- |
instance Throws MidException (Caught TopException l) -- |
-- MidException
instance Throws ChildException (Caught MidException l) -- |
instance Throws ChildException (Caught TopException l) -- |
-- ChildExceptionNote that SomeException is automatically an ancestor of every other exception type.
Instances
| UncaughtException e => Throws e NoExceptions Source # | |
Defined in Control.Monad.Exception.Base | |
| Exception e => Throws e AnyException Source # | |
Defined in Control.Monad.Exception.Base | |
| Exception e => Throws e (Caught SomeException l) Source # |
|
Defined in Control.Monad.Exception.Throws | |
| Exception e => Throws e (Caught e l) Source # | |
Defined in Control.Monad.Exception.Throws | |
| Throws e l => Throws e (Caught e' l) Source # | |
Defined in Control.Monad.Exception.Throws | |
| Throws SomeException (Caught SomeException l) Source # | |
Defined in Control.Monad.Exception.Throws | |
A type level witness of a exception handler.
Instances
| Exception e => Throws e (Caught SomeException l) Source # |
|
Defined in Control.Monad.Exception.Throws | |
| Exception e => Throws e (Caught e l) Source # | |
Defined in Control.Monad.Exception.Throws | |
| Throws e l => Throws e (Caught e' l) Source # | |
Defined in Control.Monad.Exception.Throws | |
| Throws SomeException (Caught SomeException l) Source # | |
Defined in Control.Monad.Exception.Throws | |
| (Exception e, Monad m) => MonadCatch e (EMT (Caught e l) m) (EMT l m) Source # | |
| (Exception e, MonadBaseControl IO m) => MonadCatch e (EMT (Caught e l) m) (EMT l m) Source # | |
Exception primitives
rethrow :: (Throws e l, Monad m) => CallTrace -> e -> EMT l m a Source #
Rethrow an exception keeping the call trace
catch :: (Exception e, Monad m) => EMT (Caught e l) m a -> (e -> EMT l m a) -> EMT l m a Source #
The catch primitive
catchWithSrcLoc :: (Exception e, Monad m) => EMT (Caught e l) m a -> (CallTrace -> e -> EMT l m a) -> EMT l m a Source #
Catch and exception and observe the stack trace. If on top of the IO monad, this will also capture asynchronous exceptions
finally :: Monad m => EMT l m a -> EMT l m b -> EMT l m a Source #
Sequence two computations discarding the result of the second one. If the first computation rises an exception, the second computation is run and then the exception is rethrown.
onException :: Monad m => EMT l m a -> EMT l m b -> EMT l m a Source #
Like finally, but performs the second computation only when the first one rises an exception TODO asynchronous exceptions! This needs to be moved to Monad
wrapException :: (Exception e, Throws e' l, Monad m) => (e -> e') -> EMT (Caught e l) m a -> EMT l m a Source #
Capture an exception e, wrap it, and rethrow. Keeps the original monadic call trace.
data FailException Source #
FailException is thrown by Monad fail
Constructors
| FailException String |
Instances
| Show FailException Source # | |
Defined in Control.Monad.Exception.Base Methods showsPrec :: Int -> FailException -> ShowS # show :: FailException -> String # showList :: [FailException] -> ShowS # | |
| Exception FailException Source # | |
Defined in Control.Monad.Exception.Base Methods toException :: FailException -> SomeException # fromException :: SomeException -> Maybe FailException # displayException :: FailException -> String # | |
data MonadZeroException Source #
MonadZeroException is thrown by MonadPlus mzero
Constructors
| MonadZeroException |
Instances
| Show MonadZeroException Source # | |
Defined in Control.Monad.Exception.Base Methods showsPrec :: Int -> MonadZeroException -> ShowS # show :: MonadZeroException -> String # showList :: [MonadZeroException] -> ShowS # | |
| Exception MonadZeroException Source # | |
Defined in Control.Monad.Exception.Base Methods toException :: MonadZeroException -> SomeException # fromException :: SomeException -> Maybe MonadZeroException # | |
Reexports
class (Typeable e, Show e) => Exception e where #
Any type that you wish to throw or catch as an exception must be an
instance of the Exception class. The simplest case is a new exception
type directly below the root:
data MyException = ThisException | ThatException
deriving Show
instance Exception MyExceptionThe default method definitions in the Exception class do what we need
in this case. You can now throw and catch ThisException and
ThatException as exceptions:
*Main> throw ThisException `catch` \e -> putStrLn ("Caught " ++ show (e :: MyException))
Caught ThisException
In more complicated examples, you may wish to define a whole hierarchy of exceptions:
---------------------------------------------------------------------
-- Make the root exception type for all the exceptions in a compiler
data SomeCompilerException = forall e . Exception e => SomeCompilerException e
instance Show SomeCompilerException where
show (SomeCompilerException e) = show e
instance Exception SomeCompilerException
compilerExceptionToException :: Exception e => e -> SomeException
compilerExceptionToException = toException . SomeCompilerException
compilerExceptionFromException :: Exception e => SomeException -> Maybe e
compilerExceptionFromException x = do
SomeCompilerException a <- fromException x
cast a
---------------------------------------------------------------------
-- Make a subhierarchy for exceptions in the frontend of the compiler
data SomeFrontendException = forall e . Exception e => SomeFrontendException e
instance Show SomeFrontendException where
show (SomeFrontendException e) = show e
instance Exception SomeFrontendException where
toException = compilerExceptionToException
fromException = compilerExceptionFromException
frontendExceptionToException :: Exception e => e -> SomeException
frontendExceptionToException = toException . SomeFrontendException
frontendExceptionFromException :: Exception e => SomeException -> Maybe e
frontendExceptionFromException x = do
SomeFrontendException a <- fromException x
cast a
---------------------------------------------------------------------
-- Make an exception type for a particular frontend compiler exception
data MismatchedParentheses = MismatchedParentheses
deriving Show
instance Exception MismatchedParentheses where
toException = frontendExceptionToException
fromException = frontendExceptionFromExceptionWe can now catch a MismatchedParentheses exception as
MismatchedParentheses, SomeFrontendException or
SomeCompilerException, but not other types, e.g. IOException:
*Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: MismatchedParentheses))
Caught MismatchedParentheses
*Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: SomeFrontendException))
Caught MismatchedParentheses
*Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: SomeCompilerException))
Caught MismatchedParentheses
*Main> throw MismatchedParentheses `catch` \e -> putStrLn ("Caught " ++ show (e :: IOException))
*** Exception: MismatchedParentheses
Minimal complete definition
Nothing
Methods
toException :: e -> SomeException #
fromException :: SomeException -> Maybe e #
displayException :: e -> String #
Render this exception value in a human-friendly manner.
Default implementation: .show
Since: base-4.8.0.0
Instances
data SomeException where #
The SomeException type is the root of the exception type hierarchy.
When an exception of type e is thrown, behind the scenes it is
encapsulated in a SomeException.
Constructors
| SomeException :: forall e. Exception e => e -> SomeException |
Instances
| Show SomeException | Since: base-3.0 |
Defined in GHC.Exception.Type Methods showsPrec :: Int -> SomeException -> ShowS # show :: SomeException -> String # showList :: [SomeException] -> ShowS # | |
| Exception SomeException | Since: base-3.0 |
Defined in GHC.Exception.Type Methods toException :: SomeException -> SomeException # fromException :: SomeException -> Maybe SomeException # displayException :: SomeException -> String # | |
| UncaughtException SomeException Source # | |
Defined in Control.Monad.Exception.Base | |
| Exception e => Throws e (Caught SomeException l) Source # |
|
Defined in Control.Monad.Exception.Throws | |
| Throws SomeException (Caught SomeException l) Source # | |
Defined in Control.Monad.Exception.Throws | |
class Monad f => Failure e (f :: Type -> Type) where #
Instances
| Failure e [] | |
Defined in Control.Failure | |
| Failure e Maybe | |
Defined in Control.Failure | |
| Exception e => Failure e IO | |
Defined in Control.Failure | |
| (MonadTrans t, Failure e m, Monad (t m)) => Failure e (t m) | Instance for all monad transformers, simply lift the |
Defined in Control.Failure | |
| Failure e (Either e) | |
Defined in Control.Failure | |
| (Exception e, Throws e l, Monad m) => Failure e (EMT l m) Source # | |
Defined in Control.Monad.Exception.Base | |