Monday, February 11, 2013

On Error Don't Explode

Everyone in the programming world mocks VB's old On Error Resume Next feature, saying it led to "cowboy coding". But you know what? The old horse had its merits!

The next time you're frustrated by Java's checked exceptions, think what fun it would be if you could write some code and not have to catch every single eventuality that whoever designed your API could think of... seriously, do I really need to catch a MalformedUriException when I'm just passing the constant string "www.wikipedia.org" to the Uri class constructor?

.NET, while it doesn't have checked exceptions like Java, has problems of its own. It's like a crotchety old uncle who is hard of hearing, so if you don't say what you want exactly right, it spews a stream of profanity in your face. It's almost worse than Java, since (although it's easier to code), you live in constant fear of runtime exceptions crashing your production code, so you are forced to spend all your time writing unit tests (not that those are unnecessary in Java, but they're more needed, in my opinion, in .NET).

And then there are the dynamic languages. These are the most flexible of all, so naturally they need tons of unit tests. You can't even assume that a variable you reference actually exists in some of these languages! However they are quite a bit more flexible when it comes to data structures. While Java and .NET will scream at you if you try to add a key to a map/dictionary and the key is already present, or conversely if you try to change the value of one that's not actually there, Perl (for instance) will happily overwrite the existing key when you add, and is glad to insert the one that's not there for you. (This is primarily because there is no distinction between "add" and "change value" for Perl hashes, since both are simply assignments, but that's beside the point.)

So where can we find a happy medium? On Error Resume Next is too lenient, but Java's anal-retentiveness is too strict. .NET and Perl take various stances in the middle, but I don't think either is optimal. What we need, I'd have to say, is actually a rethinking of the whole exception handling mechanic.

Instead of specifying in our code every time we want to handle an exception, why not assume that exceptions are meant to be handled? It's the best practice, so why shouldn't our programming languages adapt to it? The only thing left is to specify how the exceptions are to be handled.

There are several ways to handle an exception:

  • ignore it
  • log it and continue
  • log it and terminate
There are of course more than these, but these are the main ones that are "sane" - sure you could terminate without logging it, but that's not very helpful!

So, the idea is, in each source code file you would specify a default exception handling behavior, which could then be overridden in individual block scopes. For instance (in pseudo-C#):

// This class is very important to the global economy! If it produces incorrect results the world is doomed! We must stop it at once if it errs even slightly!
@Catch(typeof(Exception), CatchMode.LogAndTerminate)
class ImportantBusinessLogic
{
    public static void Main()
    {
        DoImportantBusinessStuff();
        // OK, we can be a little more lenient here; sometimes the web service we are using returns nulls instead of actual data; in that case we just use the cache.
        @Catch(typeof(NullReferenceException), CatchMode.LogAndContinue)
        {
            ComputeSomeInterestRates();
        }
        SaveTheGlobalEconomyFromRuin();
    }
}

Good idea? Bad idea? Anyone care?

No comments:

Post a Comment