Friday, May 15, 2009

Continuation marks in exceptions

Rethrowing caught exceptions is an important idiom. In particular, there are lots of cases where it's important to conditionally handle an exception by handling it and then rethrowing it. In ML, I believe it's the actual definition of pattern matching on particular exception types: the pattern matching is syntactic sugar for inspecting the caught exception and conditionally rethrowing it. In standard JavaScript it's the only way to catch particular exception types, because there is no standard conditional handling form.

But a useful feature of some exception systems, like in Java, is the association of a stack trace with an exception. But when you rethrow an exception, this involves a choice: do you want a stack trace to be associated with the original continuation that threw the exception, or the one that rethrew it? There are genuine use cases for both. But in Java, the implementers have to choose one or the other for you.

PLT Scheme gives you the flexibility to choose for yourself. Exceptions are not special values in Scheme; there's an orthogonal way of reifying the control state. The PLT runtime stores stack trace information in a special continuation mark, so if you grab the current continuation mark set, you've got your hands on a value that provides DrScheme's IDE with all the information it needs to visualize the continuation.

The standard exception types in PLT Scheme are records with a field for the current continuation mark set. So when you conditionally rethrow an exception, you can either keep its version of the continuation marks, or functionally update it with the current continuation marks and throw that instead.

Afterthought: I think Java might have chosen something like a policy where an exception value saves its stack trace at the point where it's created. This gives you the ability to do both by either rethrowing the same exception (to keep the original stack trace) or wrapping the exception with a new one (to use the new stack trace). I still prefer the PLT Scheme approach, which makes the stack trace a first-class value and consequently more explicit.


Jacob Matthews said...

Actually, newer versions of Java (1.4 onwards) support "chained exceptions": If you say

try { ... }
catch (SomeException e) {
  OtherException e2 = new OtherException("it broke", e);
  throw e2;

then e2.getCause() will return e, and e2's stack trace will say "caused by:" at the end and print the non-redundant part of e's stack trace as well. Not as flexible as having raw access to continuation marks but good enough to handle the most likely use cases.

Pete Kirkham said...

> I think Java might have chosen something like a policy where an exception value saves its stack trace at the point where it's created

Almost: it saves its stack trace at the point its fillInStackTrace() method is called.