Proposal: Extensible exceptions

Isaac Dupree isaacdupree at
Sat Jul 5 20:15:46 EDT 2008

Ian Lynagh wrote:
>> In fact, this applies to catchAny too.  It should come with a strong 
>> warning, and a suggestion that any unrecognised exceptions should be 
>> re-thrown.  Most uses of catchAny are to implement an on-error action 
>> anyway, I think this ought to be provided as a combinator.  We have 
>> bracketOnError, but perhaps we should also have
>>    onException :: IO a -> IO b -> IO a
> Sounds good to me.

similar to `finally` but only for when there are exceptions.
finally :: IO a -> IO b -> IO a
bracket is to bracketOnError as finally is to... onException? perhaps we 
can call it finallyOnError so we can understand the parallelism more 
naturally? :-)  (and why is there no "bracket_OnError" :-)

But I'd think that sometimes we want to have the exception (e.g. to 
print it) even if we're `onException`, so we get a slightly more similar 
signature to catchAny, but safer because it rethrows the exception after 
the second clause completes:
onAny :: IO a -> (forall e . Exception e => e -> IO b) -> IO a
Then we encourage this as another alternative to catchAny depending on 
user's needs.  What should we call it -- onAny is bad?  Really, 
`finally` corresponds to `bracket_` -- I'm not too happy with the 
Control.Exception naming situation right now, maybe I'll try to rethink 
all the names at once and post a proposal (even if we decide we don't 
want the API breakage, it'll be useful to make sure we're not leaving 
out anything else important).

By the way, what happens if a `finally` or `onException` clause throws 
an exception?  That exception replaces the the one that we were planning 
on rethrowing?  Does this already induce a risk of accidentally deleting 
Timeout exceptions (e.g. we replace it with a DiskFull exception 
accidentally produced by logging a message, that some higher level code 
catches and then proceeds as normal)?  Or do we ignore exceptions in 
those blocks? (Similar to how exceptions in C++ destructors are just a 
Bad Idea.)  But that risks ignoring a HeapOverflow or asynchronous 
exception (killThread, timeout...)?  no it doesn't, those are just 
blocked from arriving until the end of the handling-block, and it's just 
the *synchronous* exceptions that are swallowed?  But if the handler 
does some blocking I/O and thus (unblock), does that mean we risk losing 
those exceptions again?  This confuses me a lot.  Are we any better off 
than imperative languages, e.g. because most of our code isn't in I/O 
and so it uses proper data structures (Maybe, Either, etc.), rather than 
exceptions, for legitimate computational possibilities?


More information about the Libraries mailing list