I recently had occasion to write this function:

  -- do a, if that fails do b, if that fails, raise b's exception
  (?) :: IO a -> IO a -> IO a
  a ? b = a `catch` \_ -> b 

Simple enough, but if b fails, I don't really want to raise b's
exception, I want to raise both a's exception and b's exception.  What
I'd like (in some future version of Haskell) is an IOError constructor
which lets me merge two IOErrors together and appropriate operations
to test for it and, perhaps, take it apart:

  IO.multipleErrors   :: [IOError] -> IOError
  IO.isMultipleErrors :: IOError -> Bool
  IO.ioeGetErrors     :: IOError -> [IOError]

The intention is that an IOError is a non-empty list of atomic errors
(ie any of the existing IOErrors).  Thus "multipleErrors" is
essentially concat, ioeGetErrors is (essentially) the identity
function (it returns a list of IOErrors which are guaranteed to be
non-empty) and all existing functions for testing IOErrors are
modified to be defined only over singleton lists.

With these, I could write:

  -- do a, if that fails do b, if that fails, raise b's exception
  (?) :: IO a -> IO a -> IO a
  a ? b = a `catch` \ e1 -> 
          b `catch` \ e2 ->
          ioError (multipleErrors [e1,e2])

Besides the obvious choice over function names, there's other possible
choices of primitives:

  IO.twoErrors    :: IOError -> IOError -> IOError
  -- advantage: impossible to have an empty set of primitives.

It might also be advantageous to have two separate types for atomic
errors and error sets - but I think that would break a lot of code
(and breaking exception handling code is a bad idea because it's so
hard to construct good test cases).


-- 
Alastair Reid        [EMAIL PROTECTED]        http://www2.cs.utah.edu/~reid/



Reply via email to