[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