From 561a07101ec667b429eb3efe9b561f292725a8e0 Mon Sep 17 00:00:00 2001 From: simonmar Date: Mon, 27 May 2002 14:31:08 +0000 Subject: [PATCH] [project @ 2002-05-27 14:31:06 by simonmar] Document Control.Exception and Data.Dynamic --- Control/Exception.hs | 320 ++++++++++++++++++++++++++++++++++++++++++++------ Data/Dynamic.hs | 130 +++++++++++++------- GHC/Exception.lhs | 22 +++- GHC/IOBase.lhs | 139 +++++++++++++++++++--- GHC/Read.lhs | 2 + 5 files changed, 510 insertions(+), 103 deletions(-) diff --git a/Control/Exception.hs b/Control/Exception.hs index 8a66e97..5231db4 100644 --- a/Control/Exception.hs +++ b/Control/Exception.hs @@ -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 diff --git a/Data/Dynamic.hs b/Data/Dynamic.hs index 4c44988..bf83db5 100644 --- a/Data/Dynamic.hs +++ b/Data/Dynamic.hs @@ -16,38 +16,34 @@ -- with operations for converting dynamic values into a concrete -- (monomorphic) type. -- --- The Dynamic implementation provided is closely based on code --- contained in Hugs library of the same name. --- ----------------------------------------------------------------------------- module Data.Dynamic - ( - -- dynamic type - Dynamic -- abstract, instance of: Show, Typeable - , toDyn -- :: Typeable a => a -> Dynamic - , fromDyn -- :: Typeable a => Dynamic -> a -> a - , fromDynamic -- :: Typeable a => Dynamic -> Maybe a + ( + -- * The @Dynamic@ type + Dynamic, -- abstract, instance of: Show, Typeable + + -- * Converting to and from @Dynamic@ + toDyn, -- :: Typeable a => a -> Dynamic + fromDyn, -- :: Typeable a => Dynamic -> a -> a + fromDynamic, -- :: Typeable a => Dynamic -> Maybe a - -- type representation - - , Typeable( - typeOf) -- :: a -> TypeRep + -- * Concrete Type Representations + + -- | This section is useful if you need to define your own + -- instances of 'Typeable'. - -- Dynamic defines Typeable instances for the following - -- Prelude types: [a], (), (a,b), (a,b,c), (a,b,c,d), - -- (a,b,c,d,e), (a->b), (Array a b), Bool, Char, - -- (Complex a), Double, (Either a b), Float, Handle, - -- Int, Integer, (IO a), (Maybe a), Ordering + Typeable( + typeOf), -- :: a -> TypeRep - , TypeRep -- abstract, instance of: Eq, Show, Typeable - , TyCon -- abstract, instance of: Eq, Show, Typeable + -- ** Building concrete type representations + TypeRep, -- abstract, instance of: Eq, Show, Typeable + TyCon, -- abstract, instance of: Eq, Show, Typeable - -- type representation constructors/operators: - , mkTyCon -- :: String -> TyCon - , mkAppTy -- :: TyCon -> [TypeRep] -> TypeRep - , mkFunTy -- :: TypeRep -> TypeRep -> TypeRep - , applyTy -- :: TypeRep -> TypeRep -> Maybe TypeRep + mkTyCon, -- :: String -> TyCon + mkAppTy, -- :: TyCon -> [TypeRep] -> TypeRep + mkFunTy, -- :: TypeRep -> TypeRep -> TypeRep + applyTy, -- :: TypeRep -> TypeRep -> Maybe TypeRep -- -- let fTy = mkTyCon "Foo" in show (mkAppTy (mkTyCon ",,") @@ -85,9 +81,16 @@ unsafeCoerce = unsafeCoerce# #include "Dynamic.h" --- The dynamic type is represented by Dynamic, carrying --- the dynamic value along with its type representation: +{-| + A value of type 'Dynamic' is an object encapsulated together with its type. + A 'Dynamic' may only represent a monomorphic value; an attempt to + create a value of type 'Dynamic' from a polymorphically-typed + expression will result in an ambiguity error (see 'toDyn'). + + 'Show'ing a value of type 'Dynamic' returns a pretty-printed representation + of the object\'s type; useful for debugging. +-} data Dynamic = Dynamic TypeRep Obj instance Show Dynamic where @@ -100,8 +103,10 @@ instance Show Dynamic where data Obj = Obj -- dummy type to hold the dynamically typed value. +-- | A concrete representation of a (monomorphic) type. 'TypeRep' +-- supports reasonably efficient equality. data TypeRep - = App TyCon [TypeRep] + = App TyCon [TypeRep] | Fun TypeRep TypeRep deriving ( Eq ) @@ -122,7 +127,8 @@ instance Show TypeRep where showParen (p > 8) $ showsPrec 9 f . showString " -> " . showsPrec 8 a --- type constructors are +-- | An abstract representation of a type constructor. 'TyCon' objects can +-- be built using 'mkTyCon'. data TyCon = TyCon Int String instance Eq TyCon where @@ -131,33 +137,52 @@ instance Eq TyCon where instance Show TyCon where showsPrec _ (TyCon _ s) = showString s --- Operations for going to and from Dynamic: +-- | Converts an arbitrary value into an object of type 'Dynamic'. +-- +-- The type of the object must be an instance of 'Typeable', which +-- ensures that only monomorphically-typed objects may be converted to +-- 'Dynamic'. To convert a polymorphic object into 'Dynamic', give it +-- a monomorphic type signature. For example: +-- +-- > toDyn (id :: Int -> Int) +-- toDyn :: Typeable a => a -> Dynamic toDyn v = Dynamic (typeOf v) (unsafeCoerce v) -fromDyn :: Typeable a => Dynamic -> a -> a +-- | Converts a 'Dynamic' object back into an ordinary Haskell value of +-- the correct type. See also 'fromDynamic'. +fromDyn :: Typeable a + => Dynamic -- ^ the dynamically-typed object + -> a -- ^ a default value + -> a -- ^ returns: the value of the first argument, if + -- it has the correct type, otherwise the value of + -- the second argument. fromDyn (Dynamic t v) def | typeOf def == t = unsafeCoerce v | otherwise = def -fromDynamic :: Typeable a => Dynamic -> Maybe a +-- | Converts a 'Dynamic' object back into an ordinary Haskell value of +-- the correct type. See also 'fromDyn'. +fromDynamic + :: Typeable a + => Dynamic -- ^ the dynamically-typed object + -> Maybe a -- ^ returns: @'Just' a@, if the dyanmically-typed + -- object has the correct type (and @a@ is its value), + -- or 'Nothing' otherwise. fromDynamic (Dynamic t v) = case unsafeCoerce v of r | t == typeOf r -> Just r | otherwise -> Nothing --- To make it possible to convert values with user-defined types --- into type Dynamic, we need a systematic way of getting --- the type representation of an arbitrary type. A type --- class provides just the ticket, - +-- | The class 'Typeable' allows a concrete representation of a type to +-- be calculated. class Typeable a where typeOf :: a -> TypeRep - --- NOTE: The argument to the overloaded `typeOf' is only --- used to carry type information, and Typeable instances --- should *never* *ever* look at its value. + -- ^ Takes a value of type @a@ and returns a concrete representation + -- of that type. The /value/ of the argument should be ignored by + -- any instance of 'Typeable', so that it is safe to pass 'undefined' as + -- the argument. isTupleTyCon :: TyCon -> Bool isTupleTyCon (TyCon _ (',':_)) = True @@ -175,7 +200,21 @@ isTupleTyCon _ = False -- If this constraint does turn out to be a sore thumb, changing -- the Eq instance for TyCons is trivial. -mkTyCon :: String -> TyCon +-- | Builds a 'TyCon' object representing a type constructor. An +-- implementation of "Data.Dynamic" should ensure that the following holds: +-- +-- > mkTyCon "a" == mkTyCon "a" +-- +-- NOTE: GHC\'s implementation is quite hacky, and the above equation +-- does not necessarily hold. For defining your own instances of +-- 'Typeable', try to ensure that only one call to 'mkTyCon' exists +-- for each type constructor (put it at the top level, and annotate the +-- corresponding definition with a @NOINLINE@ pragma). +mkTyCon + :: String -- ^ the name of the type constructor (should be unique + -- in the program, so it might be wise to use the + -- fully qualified name). + -> TyCon -- ^ A unique 'TyCon' object mkTyCon str = unsafePerformIO $ do v <- readIORef uni writeIORef uni (v+1) @@ -201,9 +240,12 @@ showTuple (TyCon _ str) args = showChar '(' . go str args go _ _ = showChar ')' +-- | Applies a type constructor to a sequence of types mkAppTy :: TyCon -> [TypeRep] -> TypeRep mkAppTy tyc args = App tyc args +-- | A special case of 'mkAppTy', which applies the function type constructor to +-- a pair of types. mkFunTy :: TypeRep -> TypeRep -> TypeRep mkFunTy f a = Fun f a @@ -223,6 +265,10 @@ dynApp f x = case dynApply f x of "Can't apply function " ++ show f ++ " to argument " ++ show x) +-- | Applies a type to a function type. Returns: @'Just' u@ if the +-- first argument represents a function of type @t -> u@ and the +-- second argument represents a function of type @t@. Otherwise, +-- returns 'Nothing'. applyTy :: TypeRep -> TypeRep -> Maybe TypeRep applyTy (Fun t1 t2) t3 | t1 == t3 = Just t2 diff --git a/GHC/Exception.lhs b/GHC/Exception.lhs index 7979d4d..5822ea8 100644 --- a/GHC/Exception.lhs +++ b/GHC/Exception.lhs @@ -109,17 +109,27 @@ bracket_ before after m = do %********************************************************* \begin{code} -#ifndef __HUGS__ +-- | Applying 'block' to a computation will +-- execute that computation with asynchronous exceptions +-- /blocked/. That is, any thread which +-- attempts to raise an exception in the current thread will be +-- blocked until asynchronous exceptions are enabled again. There\'s +-- no need to worry about re-enabling asynchronous exceptions; that is +-- done automatically on exiting the scope of +-- 'block'. block :: IO a -> IO a -block (IO io) = IO $ blockAsyncExceptions# io +-- | To re-enable asynchronous exceptions inside the scope of +-- 'block', 'unblock' can be +-- used. It scopes in exactly the same way, so on exit from +-- 'unblock' asynchronous exception delivery will +-- be disabled again. unblock :: IO a -> IO a + +#ifndef __HUGS__ +block (IO io) = IO $ blockAsyncExceptions# io unblock (IO io) = IO $ unblockAsyncExceptions# io #else --- Not implemented yet in Hugs. -block :: IO a -> IO a -block (IO io) = IO io - unblock :: IO a -> IO a unblock (IO io) = IO io #endif diff --git a/GHC/IOBase.lhs b/GHC/IOBase.lhs index 608d2b1..1497ec9 100644 --- a/GHC/IOBase.lhs +++ b/GHC/IOBase.lhs @@ -429,24 +429,87 @@ showHandle p h duplex = -- ------------------------------------------------------------------------ -- Exception datatype and operations +-- |The type of exceptions. Every kind of system-generated exception +-- has a constructor in the 'Exception' type, and values of other +-- types may be injected into 'Exception' by coercing them to +-- 'Dynamic' (see the section on Dynamic Exceptions). +-- +-- For backwards compatibility with Haskell 98, 'IOError' is a type synonym +-- for 'Exception'. data Exception - = IOException IOException -- IO exceptions - | ArithException ArithException -- Arithmetic exceptions - | ArrayException ArrayException -- Array-related exceptions - | ErrorCall String -- Calls to 'error' - | ExitException ExitCode -- Call to System.exitWith - | NoMethodError String -- A non-existent method was invoked - | PatternMatchFail String -- A pattern match / guard failure - | RecSelError String -- Selecting a non-existent field - | RecConError String -- Field missing in record construction - | RecUpdError String -- Record doesn't contain updated field - | AssertionFailed String -- Assertions - | DynException Dynamic -- Dynamic exceptions - | AsyncException AsyncException -- Externally generated errors - | BlockedOnDeadMVar -- Blocking on a dead MVar - | Deadlock -- no threads can run (raised in main thread) + = ArithException ArithException + -- ^Exceptions raised by arithmetic + -- operations. (NOTE: GHC currently does not throw + -- 'ArithException's). + | ArrayException ArrayException + -- ^Exceptions raised by array-related + -- operations. (NOTE: GHC currently does not throw + -- 'ArrayException's). + | AssertionFailed String + -- ^This exception is thrown by the + -- 'assert' operation when the condition + -- fails. The 'String' argument contains the + -- location of the assertion in the source program. + | AsyncException AsyncException + -- ^Asynchronous exceptions (see section on Asynchronous Exceptions). + | BlockedOnDeadMVar + -- ^The current thread was executing a call to + -- 'takeMVar' that could never return, because there are no other + -- references to this 'MVar'. + | Deadlock + -- ^There are no runnable threads, so the program is + -- deadlocked. The 'Deadlock' exception is + -- raised in the main thread only (see also: "Control.Concurrent"). + | DynException Dynamic + -- ^Dynamically typed exceptions (see section on Dynamic Exceptions). + | ErrorCall String + -- ^The 'ErrorCall' exception is thrown by 'error'. The 'String' + -- argument of 'ErrorCall' is the string passed to 'error' when it was + -- called. + | ExitException ExitCode + -- ^The 'ExitException' exception is thrown by 'System.exitWith' (and + -- 'System.exitFailure'). The 'ExitCode' argument is the value passed + -- to 'System.exitWith'. An unhandled 'ExitException' exception in the + -- main thread will cause the program to be terminated with the given + -- exit code. + | IOException IOException + -- ^These are the standard IO exceptions generated by + -- Haskell\'s @IO@ operations. See also "System.IO.Error". + | NoMethodError String + -- ^An attempt was made to invoke a class method which has + -- no definition in this instance, and there was no default + -- definition given in the class declaration. GHC issues a + -- warning when you compile an instance which has missing + -- methods. | NonTermination - + -- ^The current thread is stuck in an infinite loop. This + -- exception may or may not be thrown when the program is + -- non-terminating. + | PatternMatchFail String + -- ^A pattern matching failure. The 'String' argument should contain a + -- descriptive message including the function name, source file + -- and line number. + | RecConError String + -- ^An attempt was made to evaluate a field of a record + -- for which no value was given at construction time. The + -- 'String' argument gives the location of the + -- record construction in the source program. + | RecSelError String + -- ^A field selection was attempted on a constructor that + -- doesn\'t have the requested field. This can happen with + -- multi-constructor records when one or more fields are + -- missing from some of the constructors. The + -- 'String' argument gives the location of the + -- record selection in the source program. + | RecUpdError String + -- ^An attempt was made to update a field in a record, + -- where the record doesn\'t have the requested field. This can + -- only occur with multi-constructor records, when one or more + -- fields are missing from some of the constructors. The + -- 'String' argument gives the location of the + -- record update in the source program. + +-- |The type of arithmetic exceptions data ArithException = Overflow | Underflow @@ -455,15 +518,38 @@ data ArithException | Denormal deriving (Eq, Ord) + +-- |Asynchronous exceptions data AsyncException = StackOverflow + -- ^The current thread\'s stack exceeded its limit. + -- Since an exception has been raised, the thread\'s stack + -- will certainly be below its limit again, but the + -- programmer should take remedial action + -- immediately. | HeapOverflow + -- ^The program\'s heap is reaching its limit, and + -- the program should take action to reduce the amount of + -- live data it has. Notes: + -- + -- * It is undefined which thread receives this exception. + -- + -- * GHC currently does not throw 'HeapOverflow' exceptions. | ThreadKilled + -- ^This exception is raised by another thread + -- calling 'killThread', or by the system + -- if it needs to terminate the thread for some + -- reason. deriving (Eq, Ord) +-- | Exceptions generated by array operations data ArrayException - = IndexOutOfBounds String -- out-of-range array access - | UndefinedElement String -- evaluating an undefined element + = IndexOutOfBounds String + -- ^An attempt was made to index an array outside + -- its declared bounds. + | UndefinedElement String + -- ^An attempt was made to evaluate an element of an + -- array that had not been initialized. deriving (Eq, Ord) stackOverflow, heapOverflow :: Exception -- for the RTS @@ -547,9 +633,26 @@ data ExitCode = ExitSuccess | ExitFailure Int -- -------------------------------------------------------------------------- -- Primitive throw +-- | Throw an exception. Exceptions may be thrown from purely +-- functional code, but may only be caught within the 'IO' monad. throw :: Exception -> a throw exception = raise# exception +-- | A variant of 'throw' that can be used within the 'IO' monad. +-- +-- Although 'ioError' has a type that is an instance of the type of 'throw', the +-- two functions are subtly different: +-- +-- > throw e `seq` return () ===> throw e +-- > ioError e `seq` return () ===> return () +-- +-- The first example will cause the exception @e@ to be raised, +-- whereas the second one won\'t. In fact, 'ioError' will only cause +-- an exception to be raised when it is used within the 'IO' monad. +-- The 'ioError' variant should be used in preference to 'throw' to +-- raise an exception within the 'IO' monad because it guarantees +-- ordering with respect to other 'IO' operations, whereas 'throw' +-- does not. ioError :: Exception -> IO a ioError err = IO $ \s -> throw err s diff --git a/GHC/Read.lhs b/GHC/Read.lhs index acc7ea2..b7b6965 100644 --- a/GHC/Read.lhs +++ b/GHC/Read.lhs @@ -120,6 +120,8 @@ readList__ readx ------------------------------------------------------------------------ -- ReadS +-- | A parser for a type @a@, represented as a function that takes a +-- 'String' and returns a list of possible parses @(a,'String')@ pairs. type ReadS a = String -> [(a,String)] ------------------------------------------------------------------------ -- 1.7.10.4