[Haskell] IO, exceptions and error handling

Graham Klyne gk at ninebynine.org
Mon Jun 14 09:34:51 EDT 2004


I'm finding that a recurring theme in my work with Haskell libraries (and 
in particular the XML libraries) is the awkwardness of handling errors 
outside the IO monad.

While it's often very easy to write some code that performs some function, 
assuming that the inputs are valid, as soon as code is required to deal 
with incorrect input, life gets more complicated.  The choice seems to be 
that the functions have to be re-modelled to return a value that 
incorporates a possible error condition, or any such error condition 
results in a failure of the calling program, removing the the calling 
program's option to effect a recovery strategy.

The exception handling system made available in the IO monad is one way out 
of this awkwardness, but it's only available when the recovery code is 
coded to run in the I/O monad (or by using unsafePerformIO).  I find this 
is problematic when I'm trying to write a general purpose library that uses 
some other general-purpose library that can raise errors.

I can't see any fundamental reason why exception handling has to occur in 
the IO monad.

E.g. I'm not aware that something like this would break the Haskell type 
assurances:

     class (Eq e, Show e, Show v) => Exception e v where
         makeExcept  :: v -> e
         catchExcept :: a -> (e->a) -> a
         throw      :: e -> a

     instance Exception IOError String where
         makeExcept  = userError
         catchExcept = catch
         throw e     = error (show e)
                       -- for Haskell implementations that allow errors
                       -- to be caught in the IO monad

I think a limited implementation may be possible using unsafePerformIO:

     data MyException = MyException String
     showEx (MyException s) = s
     instance Exception MyException String where
         makeExcept      = MyException
         catchExcept a h = unsafePerformIO $ catch (return a)
         throw e         = fail (showEx e)

...

Another approach that occurs to me is to introduce an error Monad along the 
lines of that described by Philip Wadler as "E" in his "Essence of 
functional programming" paper [1].  (Or just use Either as an error monad?, 
which is part of what I've been doing with my XML work.)

The disadvantages I see here are:
(a) it requires existing code to be modified to return the error monad value.
(b) it imposes a strict sequencing on the order of computation, which as 
far as I can see is not necessary to achieve the required error 
handling.  For example, a computation that returns a result that is not 
actually used in a subsequent computation would still cause an exception;  e.g.
     do { b <- f1   -- False
        ; c <- f2   -- raises exception
        ; d <- f3   -- value required
        ; return (if b then c else d)
        }
(I know this could be coded differently to avoid the claimed problem, but 
to my mind it still illustrates unnecessary complexity compared with:
     if f1 then f2 else f3
In effect, it requires the programmer to figure out the lazy evaluation 
sequences instead of letting the Haskell system do it.)

...

I'll note that I have reservations about using exception handling in 
imperative languages (because it disrupts the normal guarantees of 
execution flow), but I can't see any corresponding disadvatages to using 
exceptions in a purely functional language environment.

Are there any plans or thoughts for introducing more widely usable 
exception handling in "Haskell 2"?

#g
--

[1] http://homepages.inf.ed.ac.uk/wadler/papers/essence/essence.ps
     and others, linked from: 
http://homepages.inf.ed.ac.uk/wadler/topics/monads.html
     (cf. section 2.3)


------------
Graham Klyne
For email:
http://www.ninebynine.org/#Contact



More information about the Haskell mailing list