X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=Control%2FException.hs;h=d5d0e4c2ccf35c4492abf5d65c664764b14a5f7d;hb=d505d74452a79d349876bb31bf0a7003f3f203a0;hp=1be188c3a63f2111319fc55021562cb7cdd97ea0;hpb=fa80cd0e896b8cca30be1ed63a6374df830c5202;p=ghc-base.git diff --git a/Control/Exception.hs b/Control/Exception.hs index 1be188c..d5d0e4c 100644 --- a/Control/Exception.hs +++ b/Control/Exception.hs @@ -5,7 +5,7 @@ -- Module : Control.Exception -- Copyright : (c) The University of Glasgow 2001 -- License : BSD-style (see the file libraries/base/LICENSE) --- +-- -- Maintainer : libraries@haskell.org -- Stability : experimental -- Portability : non-portable (extended exceptions) @@ -25,6 +25,9 @@ -- * /Asynchronous exceptions in Haskell/, by Simon Marlow, Simon Peyton -- Jones, Andy Moran and John Reppy, in /PLDI'01/. -- +-- * /An Extensible Dynamically-Typed Hierarchy of Exceptions/, +-- by Simon Marlow, in /Haskell '06/. +-- ----------------------------------------------------------------------------- module Control.Exception ( @@ -35,23 +38,23 @@ module Control.Exception ( #else SomeException(..), #endif - Exception(..), -- instance Eq, Ord, Show, Typeable - IOException, -- instance Eq, Ord, Show, Typeable - ArithException(..), -- instance Eq, Ord, Show, Typeable - ArrayException(..), -- instance Eq, Ord, Show, Typeable + Exception(..), -- class + IOException, -- instance Eq, Ord, Show, Typeable, Exception + ArithException(..), -- instance Eq, Ord, Show, Typeable, Exception + ArrayException(..), -- instance Eq, Ord, Show, Typeable, Exception AssertionFailed(..), - AsyncException(..), -- instance Eq, Ord, Show, Typeable + AsyncException(..), -- instance Eq, Ord, Show, Typeable, Exception #if __GLASGOW_HASKELL__ || __HUGS__ NonTermination(..), NestedAtomically(..), #endif #ifdef __NHC__ - System.ExitCode(), -- instance Exception + System.ExitCode(), -- instance Exception #endif - BlockedOnDeadMVar(..), - BlockedIndefinitely(..), + BlockedIndefinitelyOnMVar(..), + BlockedIndefinitelyOnSTM(..), Deadlock(..), NoMethodError(..), PatternMatchFail(..), @@ -61,40 +64,39 @@ module Control.Exception ( ErrorCall(..), -- * Throwing exceptions - throwIO, -- :: Exception -> IO a - throw, -- :: Exception -> a - ioError, -- :: IOError -> IO a + throw, + throwIO, + ioError, #ifdef __GLASGOW_HASKELL__ - throwTo, -- :: ThreadId -> Exception -> a + throwTo, #endif -- * Catching Exceptions - -- |There are several functions for catching and examining - -- exceptions; all of them may only be used from within the - -- 'IO' monad. + -- $catching + + -- ** Catching all exceptions + + -- $catchall -- ** The @catch@ functions - catch, -- :: IO a -> (Exception -> IO a) -> IO a -#if __GLASGOW_HASKELL__ || __HUGS__ + catch, catches, Handler(..), -#endif - catchJust, -- :: (Exception -> Maybe b) -> IO a -> (b -> IO a) -> IO a + catchJust, -- ** The @handle@ functions - handle, -- :: (Exception -> IO a) -> IO a -> IO a - handleJust,-- :: (Exception -> Maybe b) -> (b -> IO a) -> IO a -> IO a + handle, + handleJust, -- ** The @try@ functions - try, -- :: IO a -> IO (Either Exception a) - tryJust, -- :: (Exception -> Maybe b) -> a -> IO (Either b a) - onException, + try, + tryJust, -- ** The @evaluate@ function - evaluate, -- :: a -> IO a + evaluate, -- ** The @mapException@ function - mapException, -- :: (Exception -> Exception) -> a -> a + mapException, -- * Asynchronous Exceptions @@ -102,12 +104,23 @@ module Control.Exception ( -- ** Asynchronous exception control - -- |The following two functions allow a thread to control delivery of + -- |The following functions allow a thread to control delivery of -- asynchronous exceptions during a critical region. - block, -- :: IO a -> IO a - unblock, -- :: IO a -> IO a - blocked, -- :: IO Bool + mask, +#ifndef __NHC__ + mask_, + uninterruptibleMask, + uninterruptibleMask_, + MaskingState(..), + getMaskingState, +#endif + + -- ** (deprecated) Asynchronous exception control + + block, + unblock, + blocked, -- *** Applying @block@ to an exception handler @@ -119,22 +132,23 @@ module Control.Exception ( -- * Assertions - assert, -- :: Bool -> a -> a + assert, -- * Utilities - bracket, -- :: IO a -> (a -> IO b) -> (a -> IO c) -> IO () - bracket_, -- :: IO a -> IO b -> IO c -> IO () + bracket, + bracket_, bracketOnError, - finally, -- :: IO a -> IO b -> IO a + finally, + onException, + ) where import Control.Exception.Base #ifdef __GLASGOW_HASKELL__ import GHC.Base -import GHC.IOBase import Data.Maybe #else import Prelude hiding (catch) @@ -144,9 +158,27 @@ import Prelude hiding (catch) import System (ExitCode()) #endif -#if __GLASGOW_HASKELL__ || __HUGS__ +-- | You need this when using 'catches'. data Handler a = forall e . Exception e => Handler (e -> IO a) +{- | +Sometimes you want to catch two different sorts of exception. You could +do something like + +> f = expr `catch` \ (ex :: ArithException) -> handleArith ex +> `catch` \ (ex :: IOException) -> handleIO ex + +However, there are a couple of problems with this approach. The first is +that having two exception handlers is inefficient. However, the more +serious issue is that the second exception handler will catch exceptions +in the first, e.g. in the example above, if @handleArith@ throws an +@IOException@ then the second exception handler will catch it. + +Instead, we provide a function 'catches', which would be used thus: + +> f = expr `catches` [Handler (\ (ex :: ArithException) -> handleArith ex), +> Handler (\ (ex :: IOException) -> handleIO ex)] +-} catches :: IO a -> [Handler a] -> IO a catches io handlers = io `catch` catchesHandler handlers @@ -156,7 +188,45 @@ catchesHandler handlers e = foldr tryHandler (throw e) handlers = case fromException e of Just e' -> handler e' Nothing -> res -#endif + +-- ----------------------------------------------------------------------------- +-- Catching exceptions + +{- $catching + +There are several functions for catching and examining +exceptions; all of them may only be used from within the +'IO' monad. + +Here's a rule of thumb for deciding which catch-style function to +use: + + * If you want to do some cleanup in the event that an exception + is raised, use 'finally', 'bracket' or 'onException'. + + * To recover after an exception and do something else, the best + choice is to use one of the 'try' family. + + * ... unless you are recovering from an asynchronous exception, in which + case use 'catch' or 'catchJust'. + +The difference between using 'try' and 'catch' for recovery is that in +'catch' the handler is inside an implicit 'block' (see \"Asynchronous +Exceptions\") which is important when catching asynchronous +exceptions, but when catching other kinds of exception it is +unnecessary. Furthermore it is possible to accidentally stay inside +the implicit 'block' by tail-calling rather than returning from the +handler, which is why we recommend using 'try' rather than 'catch' for +ordinary exception recovery. + +A typical use of 'tryJust' for recovery looks like this: + +> do r <- tryJust (guard . isDoesNotExistError) $ getEnv "HOME" +> case r of +> Left e -> ... +> Right home -> ... + +-} -- ----------------------------------------------------------------------------- -- Asynchronous exceptions @@ -183,7 +253,7 @@ easy to introduce race conditions by the over zealous use of -} {- $block_handler -There\'s an implied 'block' around every exception handler in a call +There\'s an implied 'mask' 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 @@ -193,22 +263,21 @@ 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) -> ) +> block $ \restore -> +> catch (restore (...)) +> (\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'. +there is no exception handler in this case. Don't use 'try' for +recovering from an asynchronous exception. -} {- $interruptible + #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 @@ -218,11 +287,10 @@ 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 ( +> mask $ \restore -> do > a <- takeMVar m -> catch (unblock (...)) +> catch (restore (...)) > (\e -> ...) -> ) if the 'Control.Concurrent.MVar.takeMVar' was not interruptible, then this particular @@ -234,3 +302,47 @@ until the point when the 'Control.Concurrent.MVar.takeMVar' succeeds. Similar arguments apply for other interruptible operations like 'System.IO.openFile'. -} + +{- $catchall + +It is possible to catch all exceptions, by using the type 'SomeException': + +> catch f (\e -> ... (e :: SomeException) ...) + +HOWEVER, this is normally not what you want to do! + +For example, suppose you want to read a file, but if it doesn't exist +then continue as if it contained \"\". You might be tempted to just +catch all exceptions and return \"\" in the handler. However, this has +all sorts of undesirable consequences. For example, if the user +presses control-C at just the right moment then the 'UserInterrupt' +exception will be caught, and the program will continue running under +the belief that the file contains \"\". Similarly, if another thread +tries to kill the thread reading the file then the 'ThreadKilled' +exception will be ignored. + +Instead, you should only catch exactly the exceptions that you really +want. In this case, this would likely be more specific than even +\"any IO exception\"; a permissions error would likely also want to be +handled differently. Instead, you would probably want something like: + +> e <- tryJust (guard . isDoesNotExistError) (readFile f) +> let str = either (const "") id e + +There are occassions when you really do need to catch any sort of +exception. However, in most cases this is just so you can do some +cleaning up; you aren't actually interested in the exception itself. +For example, if you open a file then you want to close it again, +whether processing the file executes normally or throws an exception. +However, in these cases you can use functions like 'bracket', 'finally' +and 'onException', which never actually pass you the exception, but +just call the cleanup functions at the appropriate points. + +But sometimes you really do need to catch any exception, and actually +see what the exception is. One example is at the very top-level of a +program, you may wish to catch any exception, print it to a logfile or +the screen, and then exit gracefully. For these cases, you can use +'catch' (or one of the other exception-catching functions) with the +'SomeException' type. +-} +