[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