{-# LANGUAGE DeriveDataTypeable, ExistentialQuantification, CPP #-}
-- |
-- It is often useful to distinguish between synchronous and asynchronous
-- exceptions. The common idiom is to run a user-supplied computation
-- catching any synchronous exceptions but allowing asynchronous exceptions
-- (such as user interrupt) pass through.
--
-- There's no way to know how — synchronously or asynchronously — an
-- exception was thrown, so we have to work around it by relying on the
-- exception type itself.
--
-- This module provides an extensible type for asynchronous exceptions
-- — 'SomeAsyncException' — as well as functions for catching synchronous
-- exceptions.
module Control.Exception.Async
(
-- * Exception class for asynchronous exceptions
-- | To mark an exception as asynchronous:
--
-- >instance Exception MyException where
-- > fromException = asyncFromException
-- > toException = asyncToException
--
-- Note that until base 4.7 (GHC 7.8) 'AsyncException' was not a subclass
-- of 'SomeAsyncException'. Use 'isAsynchronous' to recognize both
-- 'AsyncException' and 'SomeAsyncException'.
--
-- The re-exported documentation may say «Since: 4.7.0.0» — ignore
-- that. For older versions this package provides its own compatible
-- definitions.
SomeAsyncException
, asyncExceptionToException
, asyncExceptionFromException
-- * Detecting asynchronous exceptions
, isAsynchronous
-- * Catching synchronous exceptions
, catchSync
, handleSync
, trySync
)
where
import Control.Exception as E
import Control.Monad
import Data.Typeable
import Data.Maybe
#if !MIN_VERSION_base(4,7,0)
-- | Exception class for asynchronous exceptions
data SomeAsyncException
= forall e . Exception e => SomeAsyncException e
deriving Typeable
instance Exception SomeAsyncException
instance Show SomeAsyncException where
showsPrec p (SomeAsyncException e) = showsPrec p e
-- | 'toException' implementation for asynchronous exceptions
asyncExceptionToException :: Exception e => e -> SomeException
asyncExceptionToException = toException . SomeAsyncException
-- | 'fromException' implementation for asynchronous exceptions
asyncExceptionFromException :: Exception e => SomeException -> Maybe e
asyncExceptionFromException x = do
SomeAsyncException a <- fromException x
cast a
#endif
-- | Check whether an exception is asynchronous
isAsynchronous :: SomeException -> Bool
isAsynchronous e =
isJust (fromException e :: Maybe AsyncException) ||
isJust (fromException e :: Maybe SomeAsyncException)
-- | Like 'catch', but catch any synchronous exceptions; let asynchronous ones pass through
catchSync :: IO a -> (SomeException -> IO a) -> IO a
catchSync a h =
E.catch a $ \e ->
if isAsynchronous e
then throwIO e
else h e
-- | Like 'handle', but catch any synchronous exceptions; let asynchronous ones pass through
handleSync :: (SomeException -> IO a) -> IO a -> IO a
handleSync = flip catchSync
-- | Like 'try', but catch any synchronous exceptions; let asynchronous ones pass through
trySync :: IO b -> IO (Either SomeException b)
trySync a =
catchSync
(liftM Right a)
(return . Left)