Exceptions during exception unwinding

I had a question about onException & friends: what's the rationale for having: (error "foo") `onException` (error "bar") give bar and not foo? I.e. why does an exception raised during exception handling get propagated past the exception that triggered the handler? Most examples I can think for exception unwinding code would prefer the original exception be propagated -- for example, HDBC has a function which rolls back a DB transaction on exception; it implements it like so:
withTransaction conn func = do r <- onException (func conn) doRollback commit conn return r where doRollback = -- Discard any exception from (rollback conn) so original -- exception can be re-raised Control.Exception.catch (rollback conn) doRollbackHandler doRollbackHandler :: SomeException -> IO () doRollbackHandler _ = return () IMHO, it'd be easier to just write: withTransaction conn func = do r <- onException (func conn) (rollback conn) commit conn return r
This same argument applies to bracket, bracket_, bracketOnError & finally; even the common:
bracket openSomeHandle closeSomeHandle doAction If some error arises during doAction, there's a chance closeSomeHandle might fail (even a good chance, given that exception unwinding paths are usually poorly tested), and probably doAction has more accurate information about what went wrong than closeSomeHandle.
This is just a thought; I hadn't seen this discussed somewhere. I know for example that Java has the same approach as the current Control.Exception, so there must be good arguments for that too. One that I can think of: using onException to rethrow an exception as a different type, though that's what mapException is for, correct? Thanks, -Brian _________________________________________________________________ Microsoft brings you a new way to search the web. Try Bing™ now http://www.bing.com?form=MFEHPG&publ=WLHMTAG&crea=TEXT_MFEHPG_Core_tagline_try bing_1x1

Exception handling code should generally be assumed to work, so if
something goes wrong there you would normally like to know about it.
Also, there is nothing preventing you from wrapping the rescue code in
further exception handling, however, if the initial error were raised
upon encountering a second error, you would not be able to choose to
handle the second error.
This is how I see it anyway.
On Thu, Oct 1, 2009 at 11:29 AM, Brian Bloniarz
I had a question about onException & friends: what's the rationale for having: (error "foo") `onException` (error "bar")
give bar and not foo? I.e. why does an exception raised during exception handling get propagated past the exception that triggered the handler?
Most examples I can think for exception unwinding code would prefer the original exception be propagated -- for example, HDBC has a function which rolls back a DB transaction on exception; it implements it like so:
withTransaction conn func = do r <- onException (func conn) doRollback commit conn return r where doRollback = -- Discard any exception from (rollback conn) so original -- exception can be re-raised Control.Exception.catch (rollback conn) doRollbackHandler doRollbackHandler :: SomeException -> IO () doRollbackHandler _ = return () IMHO, it'd be easier to just write: withTransaction conn func = do r <- onException (func conn) (rollback conn) commit conn return r
This same argument applies to bracket, bracket_, bracketOnError & finally; even the common:
bracket openSomeHandle closeSomeHandle doAction If some error arises during doAction, there's a chance closeSomeHandle might fail (even a good chance, given that exception unwinding paths are usually poorly tested), and probably doAction has more accurate information about what went wrong than closeSomeHandle.
This is just a thought; I hadn't seen this discussed somewhere. I know for example that Java has the same approach as the current Control.Exception, so there must be good arguments for that too. One that I can think of: using onException to rethrow an exception as a different type, though that's what mapException is for, correct?
Thanks, -Brian _________________________________________________________________ Microsoft brings you a new way to search the web. Try Bing™ now http://www.bing.com?form=MFEHPG&publ=WLHMTAG&crea=TEXT_MFEHPG_Core_tagline_try bing_1x1_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Thu, 2009-10-01 at 03:29 +0000, Brian Bloniarz wrote:
I had a question about onException & friends: what's the rationale for having: (error "foo") `onException` (error "bar")
give bar and not foo?
I.e. why does an exception raised during exception handling get propagated past the exception that triggered the handler?
Because it's the obvious and sensible thing to do and it what every other mainstream language with exceptions does. The behaviour you want, to automatically discard any exceptions raised in the handler and to always re-raise the original exception can be implemented using the current semantics, but the reverse is not true.
Most examples I can think for exception unwinding code would prefer the original exception be propagated
Then do not rethrow a different exception.
This same argument applies to bracket, bracket_, bracketOnError & finally; even the common:
bracket openSomeHandle closeSomeHandle doAction If some error arises during doAction, there's a chance closeSomeHandle might fail (even a good chance, given that exception unwinding paths are usually poorly tested), and probably doAction has more accurate information about what went wrong than closeSomeHandle.
Then catch and ignore the exception from closeSomeHandle (though not all exceptions or you'd block ^C etc). That said, how will you know that closeSomeHandle ever works if you always ignore any exceptions it raises? For example in the case of your database transaction, having the transaction fail and roll back is something your application may be set up to handle. But transation failing during the rollback may be much more catastrophic and may want to be treated differently. If it fails in the rollback it might be leaking resources and at the very least you may want to log it differently from just a transaction that was successfully rolled back.
This is just a thought; I hadn't seen this discussed somewhere. I know for example that Java has the same approach as the current Control.Exception, so there must be good arguments for that too. One that I can think of: using onException to rethrow an exception as a different type, though that's what mapException is for, correct?
mapException is for the case of exceptions raised by pure code. As you say, being able to rethrow a different kind of exception, or simply handle the exception there and then are useful. I think using onException is the best one for your use case. If you really really want to ignore an exception raised by rollback then you have the tools available to do so. As I mentioned earlier do not just catch and discard all exceptions, see: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception... Duncan

<1254389201.7656.3.camel@localhost> Content-Type: text/plain; charset="Windows-1252" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0
On Thu=2C 2009-10-01 at 03:29 +0000=2C Brian Bloniarz wrote:
I.e. why does an exception raised during exception handling get propagated past the exception that triggered the handler?
Because it's the obvious and sensible thing to do and it what every other mainstream language with exceptions does.
Good to know ... C++ (the mainstream language I'm most familiar with) treats throwing from a destructor during unwinding as a fatal error.
bracket openSomeHandle closeSomeHandle doAction Then catch and ignore the exception from closeSomeHandle (though not all exceptions or you'd block ^C etc). That said=2C how will you know that closeSomeHandle ever works if you always ignore any exceptions it raises?
I was suggesting having bracket ignore exceptions from closeSomeHandle when doAction threw=2C not that it'd ignore any exceptions from closeSomeHandle. It's only the case where bracket has 2 exceptions to choose from that's ambiguous=2C and I'm saying that there's a fair amount of code out there that'd prefer to see the first one. Your point about ^C gets us i= nto an even grayer area -- maybe code would prefer to propagate whichever of the 2 exceptions is asynchronous=2C or define a criterion to choose which exception is more serious=2C who knows. All these behaviors are easy to implement using the building blocks that Control.Exception exports right now=2C you're right. So I'm free to build my own bracket variants with different behavior and that's what I'll do. I was just wondering if the default behavior that bracket/onException provide (which are themselves built from catch/block/unblock) is the one that most people want. Thanks=2C -Brian =0A= _________________________________________________________________=0A= Microsoft brings you a new way to search the web. Try Bing=99 now=0A= http://www.bing.com?form=3DMFEHPG&publ=3DWLHMTAG&crea=3DTEXT_MFEHPG_Core_ta= gline_try bing_1x1=
participants (3)
-
Brian Bloniarz
-
Duncan Coutts
-
Lyndon Maydwell