Alastair Reid has been very quiet, so I'll pipe up for him.

Here's a reasonable design for exceptions in Haskell:

* A value of Haskell type T can be
        EITHER one of the values we know and love 
               (bottom, or constructor, or function,
                depending on T),

        OR it can be a set of exceptional values.

* raise :: String -> a
  (raise s) returns a single exceptional value, named by string s

* All strict operations (case, +, etc) return the union of
  the exceptional values returned by their strict arguments
  For example, if both arguments to "+" return an exceptional value
  then "+" returns both. Similarly, any strict context.  

* handle :: (String -> IO a) -> IO a -> IO a
  (handle h a) tries to perform the action a.
  If doing so delivers a set of exceptional values then
  apply the exception handler h to the string that names
  one of them.  It is not defined which of the exceptional 
  values is picked.


The neat thing about this is that the exceptions can
be *raised* in arbitrary purely functional code, without
violating referential transparency.  The question of
which exception is chosen is done in the IO monad, where
of course it is allowed to be non-deterministic.
The implementation does not keep sets of exceptional values,
of course.  It simply propagates the first one it trips
over to the nearest enclosing handler.

(It is likely that successive runs will actually give
the same behaviour, but recompiling the program with
(say) different optimisation levels might change the order
of evaluation, and hence change which exception is tripped
over first.)

We're implementing an experimental version of this
in GHC, integrated with the IO monad exceptions, so that

        handle :: (IOError -> IO a) -> IO a -> IO a

and we add an extra constructor (UserError String) to the
IOError type for exceptions raised by raise.

Calls to "error" also show up as an exceptional value, of
course.

One merit of the system is that it chops out a tremendous
number of run-time error checks in the IO monad, since
we are now free to implement the mechanism with standard
stack-unwinding techniques.  Result: much better I/O performance.


I'd be interested to know what people think of this.

Simon



Reply via email to