Tuesday, May 23, 2006

For Java (and Lisp) nerds: Recoverable Exceptions

This one's for the computer science nerds out there. In a fit of insomnia tonight, reading the marvelous book Practical Common Lisp, I invented what I think is a new implementation pattern for Java. I call it RecoverableException, inspired by Common Lisp's condition-handling scheme, and it's pretty simple but really twists the language on its ear. The idea is this. We're writing an application, and we want to re-use a low-level function. The low-level function runs into an exception condition. It could recover from it - for example by re-establishing a database connection or something - but it doesn't know if it should. So it assumes the worst, throws the exception, and lets the calling stack most likely announce the problem to the user (who will then be responsible for correcting the problem and trying again instead of the low-level function that probably knows much more specifically what went wrong). Another solution: since the low-level function knows what it's doing at the time of the exception, it often knows how it could recover, but it doesn't know if it should because it doesn't know what the caller's intention and context is. So I imagined this code in the low-level function:
if (something bad happens) {
  throw new SomethingBadHappenedException () {
     public void recover () {
        doStuffToRecover();
        doPart2OfThisMethod();
     }
  };
} else {
  doPart2OfThisMethod();
}
What the hell is that? It's an inner-anonymous subclass of SomethingBadHappenedException, which looks like this:
public abstract class SomethingBadHappenedException extends Exception { 
   public abstract void recover();
}
and the calling method looks like this:
try {
  callLowLevelFunction();
} catch (SomethingBadHappenedException e) {
  if (it's appropriate to this context) {
     e.recover();
  }
} finally {
  go on my merry way . . .
}
For me that was pretty mind-blowing, because we're allowing the calling method to restart program execution where it left off LOWER DOWN in the call stack, which has already unwound because it threw the exception. Especially if the inner-anonymous recover() method uses final variables in the lower-level method, we've essentially frozen the lower-level method's stack frame in time, and we're talking to it after it would have otherwise disappeared. Incidentally, I haven't tested this - it's possible that once the exception unwinds the stack, we can't actually call doPart2(). I may have discovered another hiccup in the JVM (my first such discovery involved a bug in which 'this' was null, which I discovered had been fixed by java 1.3 or 1.4, and which I'll probably blog at a later date). To enrich this Recoverable Exception pattern, let's say SomethingBadHappenedException provides multiple abstract methods, rather than just the one recover() method. Now the caller can specify on a high-level how it wants the lower-level function to react to the exception, without having to know the details, which rightly belong in the lower-level method. For example, there could be a recoverAndRetry() method and a recoverOrAtLeastDontLoseData() method, that perhaps stored calculated data in a file. To twist this pretzel once again, let's say recover() takes an object, and the caller passes in an inner-anonymous class as the parameter:
catch (SomethingBadHappenedException e) {
  e.recover (new SomeCallbackThingy () {
     public void doSomething () {
        . . .
     }
  }
}
and in the lower-level method:
throw new SomethingBadHappenedException () {
  public void recover (SomeCallbackThingy thingy) {
     ... do stuff with thingy . . .
     thingy.doEvenMoreStuff();
  }
}
We've now effectively opened a two-way channel of communication between the two stack frames, folding the call stack back on itself. The two inner anonymous classes could theoretically call messages on each other indefinitely, and if each uses final variables of its enclosing method, we've created the code equivalent of a temporal wormhole. Okay, no, what's really happening is that we've called a method on an exception object, which happens to be an inner-anonymous class and so looks like it's part of the lower-level method, and the final variables don't really sit on the stack but on the heap - we haven't done anything that weird - but moments like these remind me of when I was eight and at three in the morning I thought I'd invented perpetual motion with a drawing of a fan turning a generator that powered the fan (to any excited 8-year-olds that just read that, please consult the second law of thermodynamics). I also don't care much if this pattern is useful, though that would be nice, primarily because more people would learn of it. To (probably mis-)quote. . . damn, I don't even remember whom I'm misquoting - he discovered mathematical formulae governing electromagnetic waves, and since this was in the 1800's a woman asked him "Of what use is it?"; he answered "Of what use is a newborn child?" I'm sure my little RecoverableException pattern is of even less use than a newborn child, though perhaps not less than a fully grown adult. But I thought it was cool.