
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? -Isaac