[project @ 2002-05-27 14:31:06 by simonmar]
[ghc-base.git] / Control / Exception.hs
index 8a66e97..5231db4 100644 (file)
@@ -8,31 +8,49 @@
 -- Stability   :  experimental
 -- Portability :  non-portable
 --
--- The External API for exceptions.  The functions provided in this
--- module allow catching of exceptions in the IO monad.
+-- This module provides support for raising and catching both built-in
+-- and user-defined exceptions.
 --
 -----------------------------------------------------------------------------
 
 module Control.Exception (
 
+       -- * The Exception type
        Exception(..),          -- instance Eq, Ord, Show, Typeable
        IOException,            -- instance Eq, Ord, Show, Typeable
        ArithException(..),     -- instance Eq, Ord, Show, Typeable
        ArrayException(..),     -- instance Eq, Ord, Show, Typeable
        AsyncException(..),     -- instance Eq, Ord, Show, Typeable
 
-       try,       -- :: IO a -> IO (Either Exception a)
-       tryJust,   -- :: (Exception -> Maybe b) -> a    -> IO (Either b a)
+       -- * Throwing exceptions
+       throw,          -- :: Exception -> a
+       ioError,        -- :: Exception -> IO a
+       throwTo,        -- :: ThreadId -> Exception -> a
 
+       -- * Catching Exceptions
+
+       -- |There are several functions for catching and examining
+       -- exceptions; all of them may only be used from within the
+       -- 'IO' monad.
+
+       -- ** The @catch@ functions
        catch,     -- :: IO a -> (Exception -> IO a) -> IO a
        catchJust, -- :: (Exception -> Maybe b) -> IO a -> (b -> IO a) -> IO a
 
+       -- ** The @handle@ functions
        handle,    -- :: (Exception -> IO a) -> IO a -> IO a
        handleJust,-- :: (Exception -> Maybe b) -> (b -> IO a) -> IO a -> IO a
 
+       -- ** The @try@ functions
+       try,       -- :: IO a -> IO (Either Exception a)
+       tryJust,   -- :: (Exception -> Maybe b) -> a    -> IO (Either b a)
+
+       -- ** The @evaluate@ function
        evaluate,  -- :: a -> IO a
 
-       -- Exception predicates (for tryJust, catchJust, handleJust)
+       -- ** Exception predicates
+       
+       -- $preds
 
        ioErrors,               -- :: Exception -> Maybe IOError
        arithExceptions,        -- :: Exception -> Maybe ArithException
@@ -42,34 +60,44 @@ module Control.Exception (
        asyncExceptions,        -- :: Exception -> Maybe AsyncException
        userErrors,             -- :: Exception -> Maybe String
 
-       -- Throwing exceptions
-
-       throw,          -- :: Exception -> a
-       throwTo,        -- :: ThreadId -> Exception -> a
-
-       -- Dynamic exceptions
+       -- * Dynamic exceptions
 
+       -- $dynamic
        throwDyn,       -- :: Typeable ex => ex -> b
        throwDynTo,     -- :: Typeable ex => ThreadId -> ex -> b
        catchDyn,       -- :: Typeable ex => IO a -> (ex -> IO a) -> IO a
        
-       -- Async exception control
+       -- * Asynchronous Exceptions
+
+       -- $async
+
+       -- ** Asynchronous exception control
+
+       -- |The following two functions allow a thread to control delivery of
+       -- asynchronous exceptions during a critical region.
 
         block,          -- :: IO a -> IO a
         unblock,        -- :: IO a -> IO a
 
-       -- Assertions
+       -- *** Applying @block@ to an exception handler
 
-       -- for now
-       assert,         -- :: Bool -> a -> a
+       -- $block_handler
 
-       -- Utilities
+       -- *** Interruptible operations
 
-       finally,        -- :: IO a -> IO b -> IO b
+       -- $interruptible
+
+       -- * Assertions
+
+       assert,         -- :: Bool -> a -> a
+
+       -- * Utilities
 
        bracket,        -- :: IO a -> (a -> IO b) -> (a -> IO c) -> IO ()
        bracket_,       -- :: IO a -> IO b -> IO c -> IO ()
 
+       finally,        -- :: IO a -> IO b -> IO b
+
   ) where
 
 #ifdef __GLASGOW_HASKELL__
@@ -104,35 +132,118 @@ INSTANCE_TYPEABLE0(AsyncException,asyncExceptionTc,"AsyncException")
 -----------------------------------------------------------------------------
 -- Catching exceptions
 
--- GHC.Exception defines 'catchException' for us.
-
-catch    :: IO a -> (Exception -> IO a) -> IO a
-catch    =  catchException
-
-catchJust :: (Exception -> Maybe b) -> IO a -> (b -> IO a) -> IO a
+-- |This is the simplest of the exception-catching functions.  It
+-- takes a single argument, runs it, and if an exception is raised
+-- the \"handler\" is executed, with the value of the exception passed as an
+-- argument.  Otherwise, the result is returned as normal.  For example:
+--
+-- >   catch (openFile f ReadMode) 
+-- >       (\e -> hPutStr stderr (\"Couldn\'t open \"++f++\": \" ++ show e))
+--
+-- For catching exceptions in pure (non-'IO') expressions, see the
+-- function 'evaluate'.
+--
+-- Note that due to Haskell\'s unspecified evaluation order, an
+-- expression may return one of several possible exceptions: consider
+-- the expression @error \"urk\" + 1 \`div\` 0@.  Does
+-- 'catch' execute the handler passing
+-- @ErrorCall \"urk\"@, or @ArithError DivideByZero@?
+--
+-- The answer is \"either\": 'catch' makes a
+-- non-deterministic choice about which exception to catch.  If you
+-- call it again, you might get a different exception back.  This is
+-- ok, because 'catch' is an 'IO' computation.
+--
+-- Note that 'catch' catches all types of exceptions, and is generally
+-- used for \"cleaning up\" before passing on the exception using
+-- 'ioError'.  It is not good practice to discard the exception and
+-- continue, without first checking the type of the exception (it
+-- might be a 'ThreadKilled', for example).  In this case it is usually better
+-- to use 'catchJust' and select the kinds of exceptions to catch.
+--
+-- Also note that The "Prelude" also exports a
+-- function called 'catch' which has the same type as
+-- 'Exception.catch', the difference being that the
+-- "Prelude" version only catches the IO and user
+-- families of exceptions (as required by Haskell 98).  We recommend
+-- either hiding the "Prelude" version of
+-- 'catch' when importing
+-- "Control.Exception", or importing
+-- "Control.Exception" qualified, to avoid name-clashes.
+
+catch          :: IO a                 -- ^ The computation to run
+       -> (Exception -> IO a)  -- ^ Handler to invoke if an exception is raised
+       -> IO a                 
+catch =  GHC.Exception.catchException
+
+-- | The function 'catchJust' is like 'catch', but it takes an extra
+-- argument which is an /exception predicate/, a function which
+-- selects which type of exceptions we\'re interested in.  There are
+-- some predefined exception predicates for useful subsets of
+-- exceptions: 'ioErrors', 'arithExceptions', and so on.  For example,
+-- to catch just calls to the 'error' function, we could use
+--
+-- >   result \<- catchJust errorCalls thing_to_try handler
+--
+-- Any other exceptions which are not matched by the predicate
+-- are re-raised, and may be caught by an enclosing
+-- 'catch' or 'catchJust'.
+catchJust
+       :: (Exception -> Maybe b) -- ^ Predicate to select exceptions
+       -> IO a                   -- ^ Computation to run
+       -> (b -> IO a)            -- ^ Handler
+       -> IO a
 catchJust p a handler = catch a handler'
   where handler' e = case p e of 
                        Nothing -> throw e
                        Just b  -> handler b
 
+-- | A version of 'catch' with the arguments swapped around; useful in
+-- situations where the code for the handler is shorter.  For example:
+--
+-- >   do handle (\e -> exitWith (ExitFailure 1)) $
+-- >     ...
 handle    :: (Exception -> IO a) -> IO a -> IO a
 handle     =  flip catch
 
+-- | A version of 'catchJust' with the arguments swapped around (see
+-- 'handle').
 handleJust :: (Exception -> Maybe b) -> (b -> IO a) -> IO a -> IO a
 handleJust p =  flip (catchJust p)
 
 -----------------------------------------------------------------------------
 -- evaluate
 
+-- | Forces its argument to be evaluated, and returns the result in
+-- the 'IO' monad.  It can be used to order evaluation with respect to
+-- other 'IO' operations; its semantics are given by
+--
+-- >   evaluate undefined `seq` return ()  ==> return ()
+-- >   catch (evaluate undefined) (\e -> return ())  ==> return ()
+--
+-- NOTE: @(evaluate a)@ is /not/ the same as @(a \`seq\` return a)@.
 evaluate :: a -> IO a
-evaluate a = a `seq` return a
+evaluate a = IO $ \s -> a `seq` (# s, a #)
 
 -----------------------------------------------------------------------------
 -- 'try' and variations.
 
+-- | Similar to 'catch', but returns an 'Either' result which is
+-- @(Right a)@ if no exception was raised, or @(Left e)@ if an
+-- exception was raised and its value is @e@.
+--
+-- >  try a = catch (Right \`liftM\` a) (return . Left)
+--
+-- Note: as with 'catch', it is only polite to use this variant if you intend
+-- to re-throw the exception after performing whatever cleanup is needed.
+-- Otherwise, 'tryJust' is generally considered to be better.
+--
 try :: IO a -> IO (Either Exception a)
 try a = catch (a >>= \ v -> return (Right v)) (\e -> return (Left e))
 
+-- | A variant of 'try' that takes an exception predicate to select
+-- which exceptions are caught (c.f. 'catchJust').  If the exception
+-- does not match the predicate, it is re-thrown.
 tryJust :: (Exception -> Maybe b) -> IO a -> IO (Either b a)
 tryJust p a = do
   r <- try a
@@ -143,23 +254,32 @@ tryJust p a = do
                        Just b  -> return (Left b)
 
 -----------------------------------------------------------------------------
--- Dynamic exception types.  Since one of the possible kinds of exception
--- is a dynamically typed value, we can effectively have polymorphic
--- exceptions.
-
--- throwDyn will raise any value as an exception, provided it is in the
--- Typeable class (see Dynamic.lhs).  
+-- Dynamic exceptions
 
--- catchDyn will catch any exception of a given type (determined by the
--- handler function).  Any raised exceptions that don't match are
--- re-raised.
+-- $dynamic
+-- Because the 'Exception' datatype is not extensible, there is an
+-- interface for throwing and catching exceptions of type 'Dynamic'
+-- (see "Data.Dynamic") which allows exception values of any type in
+-- the 'Typeable' class to be thrown and caught.
 
+-- | Raise any value as an exception, provided it is in the
+-- 'Typeable' class.
 throwDyn :: Typeable exception => exception -> b
 throwDyn exception = throw (DynException (toDyn exception))
 
+-- | A variant of 'throwDyn' that throws the dynamic exception to an
+-- arbitrary thread (c.f. 'throwTo').
 throwDynTo :: Typeable exception => ThreadId -> exception -> IO ()
 throwDynTo t exception = throwTo t (DynException (toDyn exception))
 
+-- | Catch dynamic exceptions of the required type.  All other
+-- exceptions are re-thrown, including dynamic exceptions of the wrong
+-- type.
+--
+-- When using dynamic exceptions it is advisable to define a new
+-- datatype to use for your exception type, to avoid possible clashes
+-- with dynamic exceptions used in other libraries.
+--
 catchDyn :: Typeable exception => IO a -> (exception -> IO a) -> IO a
 catchDyn m k = catchException m handle
   where handle ex = case ex of
@@ -172,6 +292,11 @@ catchDyn m k = catchException m handle
 -----------------------------------------------------------------------------
 -- Exception Predicates
 
+-- $preds
+-- These pre-defined predicates may be used as the first argument to
+-- 'catchJust', 'tryJust', or 'handleJust' to select certain common
+-- classes of exceptions.
+
 ioErrors               :: Exception -> Maybe IOError
 arithExceptions        :: Exception -> Maybe ArithException
 errorCalls             :: Exception -> Maybe String
@@ -204,7 +329,30 @@ userErrors _ = Nothing
 -----------------------------------------------------------------------------
 -- Some Useful Functions
 
-bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
+-- | When you want to acquire a resource, do some work with it, and
+-- then release the resource, it is a good idea to use 'bracket',
+-- because 'bracket' will install the necessary exception handler to
+-- release the resource in the event that an exception is raised
+-- during the computation.  If an exception is raised, then 'bracket' will 
+-- re-raise the exception (after performing the release).
+--
+-- A common example is opening a file:
+--
+-- > bracket
+-- >   (openFile "filename" ReadMode)
+-- >   (hClose)
+-- >   (\handle -> do { ... })
+--
+-- The arguments to 'bracket' are in this order so that we can partially apply 
+-- it, e.g.:
+--
+-- > withFile name = bracket (openFile name) hClose
+--
+bracket 
+       :: IO a         -- ^ computation to run first (\"acquire resource\")
+       -> (a -> IO b)  -- ^ computation to run last (\"release resource\")
+       -> (a -> IO c)  -- ^ computation to run in-between
+       -> IO c         -- returns the value from the in-between computation
 bracket before after thing =
   block (do
     a <- before 
@@ -215,9 +363,14 @@ bracket before after thing =
     return r
  )
    
--- finally is an instance of bracket, but it's quite common
--- so we give the specialised version for efficiency.
-finally :: IO a -> IO b -> IO a
+
+-- | A specialised variant of 'bracket' with just a computation to run
+-- afterward.
+-- 
+finally :: IO a                -- ^ computation to run first
+       -> IO b         -- ^ computation to run afterward (even if an exception 
+                       -- was raised)
+       -> IO a         -- returns the value from the first computation
 a `finally` sequel =
   block (do
     r <- catch 
@@ -227,5 +380,98 @@ a `finally` sequel =
     return r
   )
 
+-- | A variant of 'bracket' where the return value from the first computation
+-- is not required.
 bracket_ :: IO a -> IO b -> IO c -> IO c
 bracket_ before after thing = bracket before (const after) (const thing)
+
+-- -----------------------------------------------------------------------------
+-- Asynchronous exceptions
+
+{- $async
+
+Asynchronous exceptions are so-called because they arise due to
+external influences, and can be raised at any point during execution.
+'StackOverflow' and 'HeapOverflow' are two examples of
+system-generated asynchronous exceptions.
+
+The primary source of asynchronous exceptions, however, is
+'throwTo':
+
+>  throwTo :: ThreadId -> Exception -> IO ()
+
+'throwTo' (also 'throwDynTo' and 'Concurrent.killThread') allows one
+running thread to raise an arbitrary exception in another thread.  The
+exception is therefore asynchronous with respect to the target thread,
+which could be doing anything at the time it receives the exception.
+Great care should be taken with asynchronous exceptions; it is all too
+easy to introduce race conditions by the over zealous use of
+'throwTo'.
+-}
+
+{- $block_handler
+There\'s an implied 'block' around every exception handler in a call
+to one of the 'catch' family of functions.  This is because that is
+what you want most of the time - it eliminates a common race condition
+in starting an exception handler, because there may be no exception
+handler on the stack to handle another exception if one arrives
+immediately.  If asynchronous exceptions are blocked on entering the
+handler, though, we have time to install a new exception handler
+before being interrupted.  If this weren\'t the default, one would have
+to write something like
+
+>      block (
+>           catch (unblock (...))
+>                      (\e -> handler)
+>      )
+
+If you need to unblock asynchronous exceptions again in the exception
+handler, just use 'unblock' as normal.
+
+Note that 'try' and friends /do not/ have a similar default, because
+there is no exception handler in this case.  If you want to use 'try'
+in an asynchronous-exception-safe way, you will need to use
+'block'.
+-}
+
+{- $interruptible
+
+Some operations are /interruptible/, which means that they can receive
+asynchronous exceptions even in the scope of a 'block'.  Any function
+which may itself block is defined as interruptible; this includes
+'takeMVar' (but not 'tryTakeMVar'), and most operations which perform
+some I\/O with the outside world..  The reason for having
+interruptible operations is so that we can write things like
+
+>      block (
+>         a <- takeMVar m
+>         catch (unblock (...))
+>               (\e -> ...)
+>      )
+
+if the 'takeMVar' was not interruptible, then this particular
+combination could lead to deadlock, because the thread itself would be
+blocked in a state where it can\'t receive any asynchronous exceptions.
+With 'takeMVar' interruptible, however, we can be
+safe in the knowledge that the thread can receive exceptions right up
+until the point when the 'takeMVar' succeeds.
+Similar arguments apply for other interruptible operations like
+'IO.openFile'.
+-}
+
+-- -----------------------------------------------------------------------------
+-- Assert
+
+#ifdef __HADDOCK__
+-- | If the first argument evaluates to 'True', then the result is the
+-- second argument.  Otherwise an 'Assertion' exception is raised,
+-- containing a 'String' with the source file and line number of the
+-- call to assert.
+--
+-- Assertions can normally be turned on or off with a compiler flag
+-- (for GHC, assertions are normally on unless the @-fignore-asserts@
+-- option is give).  When assertions are turned off, the first
+-- argument to 'assert' is ignored, and the second argument is
+-- returned as the result.
+assert :: Bool -> a -> a
+#endif