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

_______________________________________________
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell

Reply via email to