monad-finally-0.1.2.1: src/Control/Monad/Exception.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ImpredicativeTypes #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
-- | A generalization of "Control.Exception" from the 'IO' monad to
-- 'MonadRecover' 'SomeException'.
module Control.Monad.Exception
(
-- * Throwing exceptions
MonadThrow
, exception
, throw
, throwIO
, ioError
, throwTo
-- * Catching exceptions
, MonadCatch
, catch
, handle
, catchJust
, handleJust
, Handler(..)
, catches
, handles
, try
, tryJust
, evaluateIO
-- * Asynchronous exception control
, MonadMask(..)
, liftGetMaskingState
, liftWithMaskingState
, mask
, mask_
, uninterruptibleMask
, uninterruptibleMask_
, interruptible
, allowInterrupt
-- * Re-exports
, module Control.Monad.Abort.Class
, module Control.Monad.Finally
, module Control.Exception
) where
#if !MIN_VERSION_base(4,6,0)
import Prelude hiding (catch)
#else
import Prelude hiding (ioError)
#endif
import Data.Monoid (Monoid)
import Control.Applicative (Applicative, (<$>))
import Control.Monad (join, liftM)
import Control.Monad.Base
import Control.Monad.Trans.Class
import Control.Monad.Trans.Control (MonadTransControl(..))
import Control.Monad.Trans.Abort hiding (abort, recover)
import Control.Monad.Trans.Finish
import Control.Monad.Trans.Maybe
#if !MIN_VERSION_transformers(0,6,0)
import Control.Monad.Trans.List
import Control.Monad.Trans.Error
#endif
import Control.Monad.Trans.Except
import Control.Monad.Trans.Reader
import qualified Control.Monad.Trans.State.Lazy as L
import qualified Control.Monad.Trans.State.Strict as S
import qualified Control.Monad.Trans.Writer.Lazy as L
import qualified Control.Monad.Trans.Writer.Strict as S
import qualified Control.Monad.Trans.RWS.Lazy as L
import qualified Control.Monad.Trans.RWS.Strict as S
import Control.Monad.Abort.Class
import Control.Monad.Finally
import Control.Concurrent (ThreadId)
import Control.Exception hiding (
evaluate, throw, throwIO, ioError, throwTo, catch, catchJust, handle,
handleJust, Handler(..), catches, try, tryJust, finally, onException,
#if !MIN_VERSION_base(4,7,0)
block, unblock, blocked,
#endif
getMaskingState, mask, mask_, uninterruptibleMask, uninterruptibleMask_,
#if MIN_VERSION_base(4,9,0)
interruptible,
#endif
allowInterrupt, bracket, bracket_, bracketOnError)
import qualified Control.Exception as E
import GHC.Base (maskAsyncExceptions#, maskUninterruptible#,
unmaskAsyncExceptions#)
import GHC.IO (IO(..))
-- | An alias for 'MonadAbort' 'SomeException' /μ/.
type MonadThrow μ = MonadAbort SomeException μ
-- | Throw an exception from pure code.
exception ∷ Exception e ⇒ e → α
exception = E.throw
{-# INLINE exception #-}
-- | Throw an exception from monadic code. An alias for
-- @'abort' . 'toException'@.
throw ∷ (MonadThrow μ, Exception e) ⇒ e → μ α
throw = abort . toException
{-# INLINE throw #-}
-- | Thow an exception from the 'IO' monad.
throwIO ∷ (MonadBase IO μ, Exception e) ⇒ e → μ α
throwIO = liftBase . E.throwIO
{-# INLINE throwIO #-}
-- | Raise an 'IOError' in the 'IO' monad.
ioError ∷ MonadBase IO μ ⇒ IOError → μ α
ioError = liftBase . E.ioError
{-# INLINE ioError #-}
-- | Raise an exception in the target thread. See 'E.throwTo'.
throwTo ∷ (MonadBase IO μ, Exception e) ⇒ ThreadId → e → μ ()
throwTo = fmap liftBase . E.throwTo
{-# INLINE throwTo #-}
-- | An alias for 'MonadRecover' 'SomeException' /μ/.
type MonadCatch μ = MonadRecover SomeException μ
-- | Recover from the specified type of exceptions.
catch ∷ (MonadCatch μ, Exception e) ⇒ μ α → (e → μ α) → μ α
catch m h = recover m $ \e → maybe (throw e) h (fromException e)
{-# INLINE catch #-}
-- | An alias for @'flip' 'catch'@.
handle ∷ (MonadCatch μ, Exception e) ⇒ (e → μ α) → μ α → μ α
handle = flip catch
{-# INLINE handle #-}
-- | Recover from exceptions that satisfy the provided predicate.
catchJust ∷ (MonadCatch μ, Exception e)
⇒ (e → Maybe β) -- ^ Exception predicate
→ μ α -- ^ Main computation
→ (β → μ α) -- ^ Exception handler
→ μ α
catchJust f m h = catch m $ \e → maybe (throw e) h $ f e
{-# INLINE catchJust #-}
-- | An alias for @'flip' . 'catchJust'@.
handleJust ∷ (MonadCatch μ, Exception e)
⇒ (e → Maybe β) → (β → μ α) → μ α → μ α
handleJust = flip . catchJust
{-# INLINE handleJust #-}
-- | Exception handler.
data Handler μ α = ∀ e . Exception e ⇒ Handler (e → μ α)
instance Functor μ ⇒ Functor (Handler μ) where
fmap f (Handler h) = Handler (fmap f . h)
{-# INLINE fmap #-}
-- | Recover from exceptions by sequentually trying to apply the provided
-- handlers.
catches ∷ MonadCatch μ ⇒ μ α → [Handler μ α] → μ α
catches m = recover m . hl
where hl [] e = abort e
hl (Handler h : hs) e = maybe (hl hs e) h $ fromException e
-- | An alias for @'flip' 'catches'@.
handles ∷ MonadCatch μ ⇒ [Handler μ α] → μ α → μ α
handles = flip catches
{-# INLINE handles #-}
-- | Recover from exceptions of the spesified type, wrapping them into
-- 'Left'.
try ∷ (MonadCatch μ, Exception e) ⇒ μ α → μ (Either e α)
try m = catch (Right <$> m) (return . Left)
{-# INLINE try #-}
-- | Recover from exceptions that satisfy the provided predicate, wrapping
-- them into 'Left'.
tryJust ∷ (MonadCatch μ, Exception e)
⇒ (e → Maybe β) -- ^ Exception predicate
→ μ α -- ^ Main compuration
→ μ (Either β α)
tryJust h m = catch (Right <$> m) $ \e →
maybe (throw e) (return . Left) (h e)
{-# INLINE tryJust #-}
-- | Evalute the argument to weak head normal form.
evaluateIO ∷ MonadBase IO μ ⇒ α → μ α
evaluateIO = liftBase . E.evaluate
{-# INLINE evaluateIO #-}
-- | A class of monads that support masking of asynchronous exceptions.
class (Applicative μ, Monad μ) ⇒ MonadMask μ where
-- | Get the current masking state.
getMaskingState ∷ μ MaskingState
default getMaskingState ∷ (MonadMask η, MonadTrans t, μ ~ t η)
⇒ μ MaskingState
getMaskingState = liftGetMaskingState
{-# INLINE getMaskingState #-}
-- | Run the provided computation with the specified 'MaskingState'.
withMaskingState ∷ MaskingState → μ α → μ α
default withMaskingState ∷ (MonadMask η, MonadTransControl t, μ ~ t η)
⇒ MaskingState → μ α → μ α
withMaskingState = liftWithMaskingState
{-# INLINE withMaskingState #-}
-- | Lift 'getMaskingState' through a monad transformer.
liftGetMaskingState ∷ (MonadMask μ, MonadTrans t) ⇒ t μ MaskingState
liftGetMaskingState = lift getMaskingState
{-# INLINE liftGetMaskingState #-}
-- | Lift 'withMaskingState' through a monad transformer.
liftWithMaskingState ∷ (MonadTransControl t, MonadMask μ, Monad (t μ))
⇒ MaskingState → t μ α → t μ α
liftWithMaskingState ms m =
join $ liftM (restoreT . return) $ liftWith $ \run →
withMaskingState ms (run m)
{-# INLINE liftWithMaskingState #-}
instance MonadMask IO where
getMaskingState = E.getMaskingState
withMaskingState Unmasked (IO io) = IO $ unmaskAsyncExceptions# io
withMaskingState MaskedInterruptible (IO io) = IO $ maskAsyncExceptions# io
withMaskingState MaskedUninterruptible (IO io) = IO $ maskUninterruptible# io
instance MonadMask μ ⇒ MonadMask (MaybeT μ)
#if !MIN_VERSION_transformers(0,6,0)
instance MonadMask μ ⇒ MonadMask (ListT μ)
#endif
instance MonadMask μ ⇒ MonadMask (AbortT e μ)
instance MonadMask μ ⇒ MonadMask (FinishT β μ)
#if !MIN_VERSION_transformers(0,6,0)
instance (MonadMask μ, Error e) ⇒ MonadMask (ErrorT e μ)
#endif
instance MonadMask μ ⇒ MonadMask (ExceptT e μ)
instance MonadMask μ ⇒ MonadMask (ReaderT r μ)
instance MonadMask μ ⇒ MonadMask (L.StateT s μ)
instance MonadMask μ ⇒ MonadMask (S.StateT s μ)
instance (MonadMask μ, Monoid w) ⇒ MonadMask (L.WriterT w μ)
instance (MonadMask μ, Monoid w) ⇒ MonadMask (S.WriterT w μ)
instance (MonadMask μ, Monoid w) ⇒ MonadMask (L.RWST r w s μ)
instance (MonadMask μ, Monoid w) ⇒ MonadMask (S.RWST r w s μ)
-- | Prevents asynchronous exceptions from being raised within the provided
-- computation. Blocking operations can still be interrupted. Supplies the
-- computation with @'withMaskingState' s@, where @s@ is the current masking
-- state.
mask ∷ ∀ μ α . MonadMask μ
⇒ ((∀ η β . MonadMask η ⇒ η β → η β) → μ α) → μ α
mask f = getMaskingState >>= \case
Unmasked →
withMaskingState MaskedInterruptible $ f (withMaskingState Unmasked)
MaskedInterruptible →
f (withMaskingState MaskedInterruptible)
MaskedUninterruptible →
f (withMaskingState MaskedUninterruptible)
-- | An alias for @'mask' . 'const'@.
mask_ ∷ ∀ μ α . MonadMask μ ⇒ μ α → μ α
mask_ = mask . const
{-# INLINE mask_ #-}
-- | Prevents asynchronous exceptions from being raised within the provided
-- computation. Also prevents blocking operations from being interrupted.
-- Supplies the computation with @'withMaskingState' s@, where @s@ is the
-- current masking state.
uninterruptibleMask ∷ MonadMask μ
⇒ ((∀ η β . MonadMask η ⇒ η β → η β) → μ α) → μ α
uninterruptibleMask f = getMaskingState >>= \case
Unmasked →
withMaskingState MaskedUninterruptible $ f (withMaskingState Unmasked)
MaskedInterruptible →
withMaskingState MaskedUninterruptible $
f (withMaskingState MaskedInterruptible)
MaskedUninterruptible →
f (withMaskingState MaskedUninterruptible)
-- | An alias for @'uninterruptibleMask' . 'const'@.
uninterruptibleMask_ ∷ MonadMask μ ⇒ μ α → μ α
uninterruptibleMask_ = uninterruptibleMask . const
{-# INLINE uninterruptibleMask_ #-}
-- | Allow asynchronous exceptions to be raised within the provided
-- computation, even if the current masking state is 'MaskedInterruptible'
-- (but not in 'MaskedUninterruptible' state).
interruptible ∷ MonadMask μ ⇒ μ α → μ α
interruptible m = getMaskingState >>= \case
Unmasked → m
MaskedInterruptible → withMaskingState Unmasked m
MaskedUninterruptible → m
-- | An alias for @'interruptible' ('return' ())@.
allowInterrupt ∷ MonadMask μ ⇒ μ ()
allowInterrupt = interruptible $ return ()
{-# INLINE allowInterrupt #-}