Nothing Exceeds Like Exceptions

As a bright-eyed computer science student, I fell in love with exceptions. Of course, growing up in C++ land, no one explained the idea of an exception type hierarchy, so when I threw them, they were usually ints. But even at that naive time, something in me recognized the value of losing all those “if errorlevel” statements. Something else recognized the tingle of a problem unanswered.

After uprooting my ratty recliner and other worldly belongings for a move to Java land, I discovered the magic of checked exceptions. Wow. The compiler tells me when I should handle an error? Brilliant. Fantastic. That must have been the itch; I could never really specify what I might throw. Then I started writing Java.

// Java
try {
    InputStream captain = new FileInputStream("kirk.txt");
} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block

Well, that was obnoxious.

# Python
science = open('spock.txt')

Alright, Python is arguably a scripting language, ill-equipped to handle the rigors of large pieces of software; that that is not the point. Here, Python and Java do almost the same thing, but Python does it with only one line. The Java snippet silently suppresses an error, if you happen to not be constantly watching your output stream. So do your todo and re-throw FileNotFound as a RuntimeException. Make your code at least as fail-fast as if it were Python.

The point is also not that Python beats Java. Although strong typing may be fascism, the real question is whether the checked exception added any value.

What could I do with that exception? If I were writing a “good-enough” utility, I would want to crash swiftly and furiously. If I were writing end-user worthy code, I would have checked for the file’s existence before trying to open it. In that case, the odds of someone removing the file between my existence check and open attempt would be low enough that the method could reasonably return null, simplify my code, and disintegrate later when I actually tried to read something. My file reading code would presumably be at least as careful as my file opening code, so it could own the error handling for that situation.

So maybe the itch was actually the feeling that excellent code is almost entirely error handling. “Have your functions return error values when things go wrong, and deal with these explicitly, no matter how verbose it might be,” said Joel Spolsky, about the time I was entering my first class on object orientation. His points are true, but he proposes no solution to the need for a heart-wrenchingly ugly fail-fast mechanism in a language. For language designers, I propose this half-baked idea:

Make exceptions uncatchable.

First, this implies that you no longer need the exception class hierarchy because any throw becomes an exit with an error code. So how is this better than System.exit(1)? It gives you a stack trace. Replace all throws with asserts; asserts that really work, that is.

Second, libraries would have to use assert carefully, but when they did, they could use it as a real teaching experience. You might think the file opening method that crashed your entire process ludicrous, but you might learn to check for the file’s existence before opening it. The generated empty catch block has probably never taught anyone this lesson.

On the down side, you now have no way to record these catastrophic errors. Even if the virtual machine printed the stack trace to standard error with a dying breath, you might have forwarded that to /dev/null. Allowing a shutdown hook to intercept the error might help, but you know that would tempt you to do too much with a program in an unknown state.

Oh well. It’s still a good idea because I just thought of it.