Exceptions
Types
- Synchronous exceptions
-
Generated as a result of a failing action in IO (same thread). Usually thrown using
throwIO
. - Impure exceptions
-
Thrown in pure code by partial function. Ideally, we would not use such functions, a better practice is to return an
Either
type in this situation. - Asynchronous exceptions
-
Can occur anywhere, including in pure code. Generated when another thread or the runtime system is trying to kill the current thread (via throwTo) or report an “unrecoverable” situation like a StackOverflow.
- Interruptible actions
-
Some operations are interruptible by async exceptions even within a mask. This is the case for blocking functions such as
takeMVar
but also for most I/O operations dealing with the outside world.
Primitives
throwIO :: Exception e => e -> IO a (1)
1 | you should always prefer throwIO to throw |
try :: Exception e => IO a -> IO (Either e a)
catch :: Exception e
=> IO a -- ^ computation
-> (e -> IO a) -- ^ handler
-> IO a
|
finally
:: IO a -- ^ computation
-> IO b -- ^ computation to run afterward even if an exception was raised
-> IO a
a `finally` sequel =
mask $ \restore -> do
r <- restore a `onException` sequel
_ <- sequel
return r
-- | Like 'finally', but only performs the final action if there was an
-- exception raised by the computation.
onException :: IO a -> IO b -> IO a
onException io what =
io `catch` \e -> do _ <- what
throwIO (e :: SomeException)
bracket
:: IO a -- ^ acquire resource
-> (a -> IO b) -- ^ release resource
-> (a -> IO c) -- ^ use resource
-> IO c
bracket before after use =
mask $ \restore -> do
a <- before
r <- restore (use a) `onException` after a
_ <- after a
return r
Monad primitives
The exceptions
package defines Control.Monad.Catch
with
- MonadThrow
-
class Monad m => MonadThrow m where throwM :: Exception e => e -> m a
- MonadCatch
-
class MonadThrow m => MonadCatch m where catch :: Exception e => m a -> (e -> m a) -> m a
- MonadMask
-
class MonadCatch m => MonadMask m where mask :: ((forall a. m a -> m a) -> m b) -> m b uninterruptibleMask :: ((forall a. m a -> m a) -> m b) -> m b
-
Instances should ensure that, in the following code
f ‘finally’ g
, the actiong
is called regardless of what occurs withinf
, including async exceptions. -
ExceptT
is not an instance of MonadMask. See MonadMask vs MonadBracket
-