[project @ 2002-09-14 09:27:21 by panne]
[ghc-base.git] / GHC / IOBase.lhs
index 608d2b1..6f7d9c9 100644 (file)
@@ -17,6 +17,8 @@
 module GHC.IOBase where
 
 import GHC.ST
+import GHC.Arr -- to derive Ix class
+import GHC.Enum -- to derive Enum class
 import GHC.STRef
 import GHC.Base
 import GHC.Num -- To get fromInteger etc, needed because of -fno-implicit-prelude
@@ -55,6 +57,19 @@ Libraries - parts of hslibs/lang.
 --SDM
 -}
 
+{-|
+A value of type @'IO' a@ is a computation which, when performed,
+does some I\/O before returning a value of type @a@.  
+
+There is really only one way to \"perform\" an I\/O action: bind it to
+@Main.main@ in your program.  When your program is run, the I\/O will
+be performed.  It isn't possible to perform I\/O from an arbitrary
+function, unless that function is itself in the 'IO' monad and called
+at some point, directly or indirectly, from @Main.main@.
+
+'IO' is a monad, so 'IO' actions can be combined using either the do-notation
+or the '>>' and '>>=' operations from the 'Monad' class.
+-}
 newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))
 
 unIO :: IO a -> (State# RealWorld -> (# State# RealWorld, a #))
@@ -116,9 +131,32 @@ free of side effects and independent of its environment.
 If the I\/O computation wrapped in 'unsafePerformIO'
 performs side effects, then the relative order in which those side
 effects take place (relative to the main I\/O trunk, or other calls to
-'unsafePerformIO') is indeterminate.  
-
-However, it is less well known that
+'unsafePerformIO') is indeterminate.  You have to be careful when 
+writing and compiling modules that use 'unsafePerformIO':
+
+  * Use @{\-\# NOINLINE foo \#-\}@ as a pragma on any function @foo@
+       that calls 'unsafePerformIO'.  If the call is inlined,
+       the I\/O may be performed more than once.
+
+  * Use the compiler flag @-fno-cse@ to prevent common sub-expression
+       elimination being performed on the module, which might combine
+       two side effects that were meant to be separate.  A good example
+       is using multiple global variables (like @test@ in the example below).
+
+  * Make sure that the either you switch off let-floating, or that the 
+       call to 'unsafePerformIO' cannot float outside a lambda.  For example, 
+       if you say:
+       @
+          f x = unsafePerformIO (newIORef [])
+       @
+       you may get only one reference cell shared between all calls to @f@.
+       Better would be
+       @
+          f x = unsafePerformIO (newIORef [x])
+       @
+       because now it can't float outside the lambda.
+
+It is less well known that
 'unsafePerformIO' is not type safe.  For example:
 
 >     test :: IORef [a]
@@ -429,24 +467,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: "Control.Exception\#DynamicExceptions").
+--
+-- 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: "Control.Exception\#AsynchronousExceptions").
+  | 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: "Control.Exception\#DynamicExceptions").
+  | 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 +556,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 +671,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
 
@@ -656,4 +797,10 @@ instance Show IOException where
       (case fn of
         Nothing -> id
         Just name -> showString "\nFile: " . showString name)
+
+-- -----------------------------------------------------------------------------
+-- IOMode type
+
+data IOMode      =  ReadMode | WriteMode | AppendMode | ReadWriteMode
+                    deriving (Eq, Ord, Ix, Enum, Read, Show)
 \end{code}