[Haskell] Better Exception Handling

Ben Rudiak-Gould Benjamin.Rudiak-Gould at cl.cam.ac.uk
Tue Nov 23 16:11:05 EST 2004

John Goerzen wrote:

 >On Tue, Nov 23, 2004 at 05:20:19PM +0000, Ben Rudiak-Gould wrote:
 >>In any case, mapException is pure, and it's good enough for most of 
the cases where one might want to catch exceptions outside the IO monad.
 >Well, I'm maving trouble wrapping my head around how I could use it in a
 >pure enviroment.  It's defined as:
 >mapException :: (Exception -> Exception) -> a -> a
 >I *think*, from reading the source, that it is returning the value 'a'
 >if there is no exception, or the mapped exception (or set of them?) if
 >there is.

Basically, in the paper, exceptions are modeled as extra values which 
inhabit every type. Bool, for example, contains the following values: 
True, False, _|_, and all nonempty /sets/ of values of type Exception.  
(The paper actually says that _|_ is the set of all exceptions, but I 
think that's an error. _|_ is quantitatively different from any set of 
exceptions.) My expression (error "x" + error "y") has the value { 
Control.Exception.ErrorCall "x", Control.Exception.ErrorCall "y" }. The 
exception-catching functions choose a single exception from the set by 
some unknown means; since they're in the IO monad it needn't be 
predictable which one they choose.

If its second argument is a set of exceptions, mapException passes each 
exception through the supplied function to get a new set, and returns 
that. Otherwise it returns its second argument unchanged.

 >That doesn't really help me, though, because I still have an exception
 >that I must catch in the IO monad.

It's useful for adding information to an exception as it propagates, or 
translating exceptions at abstraction boundaries. (Except it doesn't 
quite work, because the exceptions can hide from the handler -- see below.)

 >>will catch any exceptions that are thrown during the parsing of the
 >>configuration file. Depending on how readConfigFile is written, those
 >>exceptions may be hiding inside the returned list, where they won't be
 >>noticed until some other part of the program inspects that part of the
 >>list, at which point your exception handler is no longer in scope. The
 >I lost you here.  Isn't the exception handler just another function?
 >That is, closures would handle this just like anything else?

I didn't phrase it well. Denotationally the problem is that, e.g.,

    catch (return [1,2,undefined])
          (\e -> return [4,5,6])         === return [1,2,undefined]

whereas often (usually!) I'd prefer it be equivalent to (return 
[4,5,6]). Every exception-handling function, including mapException, 
behaves this way:

    myMapException = mapException (\e -> error "internal error")

    myMapException (throwDyn MyError)  ===  error "internal error"
    myMapException [throwDyn MyError]  ===  [throwDyn MyError]

If the first element of the list returned by that last example is ever 
demanded, MyError will be thrown, and it will /not/ be passed through 
(\e -> error "internal error"). This means that I can't 
(straightforwardly) wrap an exception handler around my function to 
catch exceptions that I don't want the user, or another part of the 
program, to see.

Of course, this isn't a bug and can't exactly be "fixed". It's just an 
interaction between the traditional throw/catch exception model and 
non-strictness that I consider to be unfortunate. Most of the cases 
where I might otherwise have used exceptions in Haskell have fallen 
afoul of this problem.

 >>A question I don't know the answer to is whether (catchJust :: a ->
 >>Maybe a), which lumps all exceptions into a Nothing return, is pure.
 >Are you proposing a hypothetical here?  (GHC's catchJust returns an
 >IO a).

Sorry, that was a braino. It's a totally different function. I should 
have called it something like exceptionToMaybe. The intended semantics is

                         / Nothing   if x is a set of exceptions
    exceptionToMaybe x = | _|_       if x is _|_
                         \ Just x    otherwise

-- Ben

More information about the Haskell mailing list