Home > Uncategorized > Exceptions Part 5: The Myth of the Exception Hierarchy

Exceptions Part 5: The Myth of the Exception Hierarchy

See the Contents Page for this series of articles

Last time we identified a serious oversight in the JVM’s exception model (that the runtime unconditionally executes finally blocks before first figuring out whether active exception is even going to be caught), so we’ve made Java look bad. Now let’s heap some abuse on object orientation itself.

Well, maybe not abuse, but certainly some much-needed moderation. It’s not uncommon to find someone defending their favourite language on the grounds that it is very object-oriented, or attacking another because it isn’t OO enough.

In truth, OO is a mixture of ideas which happen to work fairly well, as long as they are applied in the right contexts. It’s not the One True Way, and we cannot decide once and for all which is the best programming language by figuring out how object-oriented it is.

One of the tenets of OO is inheritance: a Receptionist is an Employee is a Human is a Mammal is a Vertibrate is a Chordate is an Animal is a Eukaryote is an Organism is an Object. All perfectly straightforward.

Another is that an object may (and usually will) define some operations that are intrinsic to it – it should have behaviour. Our mental picture of an object is like a physical device with buttons on it, and by pressing the buttons on the device, you can cause it to carry out some important piece of behaviour, which will depend partly on the current state of the device and may modify that state. For example, pressing the button on a camera will cause the camera to take a new picture and store it somewhere inside itself.

This seems to fit nicely with inheritance quite nicely in a lot of contexts. I might be carrying a digital camera or an old film one, and if the old one is really old, it might have a lever to pull instead of a button to press. So my life becomes a little more complicated:

void TakePicture(Camera camera) {

    if (camera instanceof FilmCamera) {
        camera.pullLevel();
    } else if (camera instanceof DigitalCamera) {
        camera.pressButton();
    }
}

It would be much nicer if the Camera base class had a method called takePicture, and each derived class would implement this in whatever way made sense. Then on a camera I could just say:

camera.takePicture();

There was something nasty about my original TakePicture method, which is that it assumed only two direct derived classes would exist. What if camera is a PolaroidCamera, which has neither a roll of film nor digital storage? My method silently does nothing in that case, which is probably not very helpful. That’s the kind of messy ad hoc decision-making that you definitely don’t want to scatter around your code.

Armed with this experience, we OO zealots feel emboldened to issue a commandment:

Thou Shalt Not Type-Switch!

A “type-switch” is something like the switch statement in C, except you decide which code block to run based on the type of an object.

This is exactly the crime I committed in my original TakePicture method, using if/else. There’s no special “switch on type” statement or keyword in Java, C#, C++ or Eiffel, because all those languages take this commandment very seriously. In fact, both C++ and Eiffel initially didn’t even include a way to find out the type of an object, in an attempt to stop programmers from writing code like TakePicture. (Both languages caved in eventually.)

Anywhere that you find yourself needing to write a type switch, so the commandment says, you must have designed your class hierarchy badly. The type-switch ought to be replaced by a simple call to a single method, and then each type can decide for itself how to implement that method.

And yet there’s a strange exception (pun intended) to this commandment:

catch (ExceptionType1 x) {
    // handle somehow
}
catch (ExceptionType2 x) {
    // handle some other way perhaps
}
catch (ExceptionType3 x) {
    // and so on
} 

That, my friends, is nothing more or less than a type switch. As OO zealots, we should be outraged by it! Instead, it should surely say:

catch (Exception x) {
    x.handleYourself();
}

Which unfortunately is pure nonsense. The whole point is that the catcher decides what action to take (if any – as we’ve seen previously, it’s essential to ignore exceptions you don’t understand).

The fault here lies not with the reality of exceptions, but with the dogma of OO. Sometimes the correct action to take depends on two (or more) things, not merely the type of one object. This is known as multiple dispatch. To someone who has been misled into thinking that OO is an all-encompassing philosophy, it can be an unnerving experience to encounter an inescapable multiple dispatch scenario.

So, exception handling is a multiple-dispatch scenario: we have the type of the exception, and also the context in which it has occurred, and so we inevitably need to use something very much like a type switch.

OO Makes Things Harder – Again!

Unfortunately, inheritance makes type switching a little more complicated than it first appears. The question “Is this object a Vertibrate?” will be answered positively for a Mammal, a Human and an Employee.

And because exceptions are classes, and classes can be inherited, inheritance must inevitably interact with an exception type switch. So a catch statement that catches a base exception class will also catch all the derived exception classes.

But the full power of inheritance seems to be put to almost no use in exception classes. In both the JVM and CLR, an Exception carries a message string that describes the error so you can pass the buck to a logging system or (in a GUI application) display it in a message box. But it’s practically unheard of for a derived exception class to re-implement that method (even though this is allowed in both frameworks); the framework provides a base class that takes care of storing the string and returning it via some kind of getter. That’s usually good enough.

Even so, class library designers can’t resist organising their exception types into hierarchies. It just seems tidier to have all the exceptions relating to security to be derived from SecurityException, doesn’t it?

But does it really make any sense to do this?

Bad vs Ugly Again

Let’s return to the Bad and Ugly exceptions. An Ugly exception is one you don’t catch, because you don’t expect it to occur, it doesn’t figure in your understanding of correct program behaviour and so you’d have no idea what your program should do next if you encounter it.

Note that we can – with a useful degree of certainty – rule that some exception types ought to be considered Most Certainly Ugly, regardless of the context. The obvious example is known as NullReferenceException in C#, and ought to have the same name in Java but instead is called NullPointerException there. What-ever! The point is, any program that catches that exception is up to something very fishy indeed. It really shouldn’t be done. In an ideal world, it wouldn’t even be possible to catch it, and indeed some exceptions are already uncatchable by default in the CLR (with each successive version of the CLR, the set of uncatchable exceptions has grown).

If we can nominate some exceptions as Most Certainly Ugly, it’s natural to ask if we can apply some symmetry and nominate some exceptions as Generally Worth Catching. The reason this is so tempting is because it brings to mind this dream code snippet:

try {

} catch (GenerallyWorthCatching x) {
    // handle somehow
}

The class library designers, in their supernatural wisdom, could somehow choose those exceptions that it makes sense to catch – regardless of the context – and derive them from GenerallyWorthCatching, and so we could simply catch that common base class. We wouldn’t ever need to refer to the documentation to find out what exception types might be thrown, or consider whether we expect them to occur. We’d barely need to think at all! Bliss.

But it’s an illusion of course. There is no symmetry here.

When you touch the file system, especially in an interactive GUI app, you usually need to catch both IOException and SecurityException, and treat those two impostors just the same. If a GUI app lets the user delete a configuration file, and it fails, this probably means the user has it open in Notepad. Usually the most helpful thing to do is display the exception’s message string and ask the user if they want to try again. It’s not a big deal, it’s a known condition that can happen, even though it’s not that common. So it’s ideal for uniform exception handling – that is, for both those expected exception types, you actually do exactly the same thing.

To the truly lazy programmer (i.e. any programmer), it’s fun to dream about a world in which both IOException and SecurityException are derived from a hypothetical GenerallyWorthCatching base class, so we could just catch that instead. Wouldn’t that be convenient?

But now forget about GUI apps with a user who can interactively fix a problem and choose to retry. Consider instead a server app.

Imagine that it has a directory on the hard drive where it keeps temporary files. The filenames are significant – they are meaningful keys to retrieve data from specific files (e.g. the data relating to Afghanistan is in a file called Afghanistan.data). For the part I’m writing, I need to delete a file. Maybe I’m going to delete Afghanistan.data so that then I can rename temp127834.data to become Afghanistan.data, which is the new version I’ve been preparing off to one side.

So the old version absolutely has to be deleted, or else the next step my application needs to perform will be screwed before it even starts. More formally: it is a precondition of the next step that the file not exist. I’ve been really careful, but what if the file is locked because I leaked a handle somewhere else?

If so, the unfortunate fact is that I have a fatal bug. I have to give up, because it invalidates all my efforts to get it right, and I know that the next thing the code will try to do will also fail, probably in a totally confusing way that obscures the underlying problem. Things can only get worse if I continue.

So here’s a situation where an IOException ought to be treated as fatal. So maybe it shouldn’t be derived from GenerallyWorthCatching after all!

Which means that it is an unrealistic dream to think that each exception could be given an intrinsic property (of any kind – a flag or a base class) that indicates whether you can expect to recover from it. It completely depends on what you were trying to do, and on what you need to do next. Only you know why you were attempting the operation, so only you know the implications should it fail.

A Warning From History

So it’s instructive to look at the documentation for ApplicationException. It says:

The exception that is thrown when a non-fatal application error occurs.

Somehow I doubt that. We just went over why it’s impossible for an exception type to be intrinsically non-fatal (i.e. Bad rather than Ugly).

It goes on to say:

The ApplicationException class differentiates between exceptions defined by applications versus exceptions defined by the system.

So this is a pretty ambitious class. It’s so ambitious, it has two contradictory ambitions, one of which is impossible anyway!

And then there’s the old problem of entropy: if something has a degree of freedom, it will wander off somewhere. In the CLR itself, there are exceptions derived from ApplicationException, so that screws up the second ambition. And they are often the kind of exception that you’d never want to catch – Most Certainly Ugly, such as System.Reflection.TargetParameterCountException.

It’s no wonder that the documentation now has a big warning label on it. The current advice (and I believe the only possible correct advice) is to derive your own exception types from Exception, the “Daddy” base class. The ability of catch to work off inheritance is basically irrelevant. Assignment compatibility of the types is not a reliable guide as to whether they would be “equivalently catch-worthy”. It is now recognised that exceptions form a flat space of peer types, that have to be treated as individuals on their own merits, not grouped in a hierarchy and dealt with en masse. (There’s probably a political analogy here…)

Shouldn’t This Be Easier?

I’m afraid that if you find yourself thinking “This should be a whole lot easier. There must be something Microsoft did wrong here. I shouldn’t have to think about all these different possibilities”, well, guess again.

The present situation is probably as simple as it can ever get. The points I’m making here are not specific to C# or the CLR, they are – pretentious as it sounds – philosophical conclusions about categorisation, hierarchy, relatedness, and the distinction between the known and the unknown. And you ain’t gonna fix all that with a better library!

Don’t Even Allow Exception Inheritance

Even though sometimes you might get lucky and the type hierarchy happens out to be convenient in your specific situation, is it really a good idea to be lazy and catch a common base class? The thing to be wary of is that libraries evolve over time.

What if a future maintainer derives a new exception from that base, and it’s one that you shouldn’t be catching in this context? Even a dynamically loaded plugin can do that!

If you think about it, catching any exception type that can be derived from is a very strange thing to do. It’s just like catching Exception, really – it’s saying that you know you can handle unknown, unspecified future situations. You must be super smart!

It could be valid, if the derived type truly indicates a failure that can be recovered from by any caller who can recover from the previously-known types derived from the base type. But that’s yet another tough thing to get right, or to explain to a team of coworkers, or a world full of dynamic plugin contributors.

Which means that not only should exceptions be derived only from Exception, but they should be sealed, so no one can derive from them. This is the only way to force exceptions to be a flat space of unrelated types. And as we’ve seen previously, you should never catch Exception.

So you have to write a lot of catch statements, which can be annoying. I will come back to that topic in a future article, as there’s some more tragic history involved. But…

Next time: What’s the deal with checked exceptions?

Advertisements
Categories: Uncategorized Tags: ,
  1. ficedula
    July 25, 2010 at 11:08 am

    In fairness, there are situations where catching a non-sealed Exception type *should* be valid, and to a limited extent can be (as you did mention briefly). If I’m trying to write out data in response to a File/Export request by the user, I don’t really care why I couldn’t save the data; whether it’s a PermissionDeniedException, SharingViolationException, DiskFullException or UserPulledTheSoddingUSBDriveOutException, doesn’t matter to me. Any FileIOExceptions, I can catch; my internal data structures are fine, I just couldn’t write out to a new file, so inform the user the export failed due to reason X and wait for more input.

    (Of course, in some situations I do care; writing to a shared file on the network, SharingViolation may be expected – back off, retry up to 3 times, another user is temporarily accessing the file – but DiskFull is both unexpected and possibly impossible to recover from as we can’t even dump our Must-Be-Saved data to another file!)

    The fact a plugin, or a new version of a library, could derive a new exception from FileIOException is a bit of a red herring. Sure, it *could* derive a HardDiskIsTotallyCorruptException which maybe I didn’t want to catch, and throw it; but then, it could also throw PermissionDeniedException in situations that aren’t due to file permissions being denied, so allowing third party plugins or libraries into your app already means your try/catch blocks might trigger inappropriately. Sealing exceptions in fact “encourages” this to some extent. There’s no technical solution to this; you just need a non-technical solution along the lines of “follow these rules or else”.

    The real problem is that exceptions are hard to use correctly. But you *could* use exception inheritance and get it right, which is more than can be said for catching Exception. The fact it’s hard to get it right is pretty much par for the course. (The fact it doesn’t *look* hard to get it right at first glance is also pretty much situation normal for exceptions, for that matter!).

  2. Ian Ringrose
    November 22, 2010 at 4:25 pm

    One good use of Exception Inheritance is when I wish to add a new exception (to provide better information to the caller) to code that is already in use. I can make the new exception be a subtype of a more general exception the old code throw, then new caller can choose to catch the new exception, but the old code is not broken by a different exception being thrown.

  3. Evan Cox
    October 5, 2015 at 9:45 pm

    What happened to Part 6!?

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: