Home > Uncategorized > Exceptions Part 1: What is the Purpose of Exceptions?

Exceptions Part 1: What is the Purpose of Exceptions?

A few months ago I wrote some long comments on an Eric Lippert blog post and ever since I’ve been meaning to distill them into something more coherent.

But I can’t! If anything my rambling notes get longer and longer. Exceptions simply aren’t that simple. Of the four big commercial development languages (C++, Java, VB.NET, C#) there are similar keywords for exceptions but significant differences in how they work. Then there are the “lightweight” platforms, JavaScript in the browser and Objective-C on Apple’s iOS, which again have the same keywords but not the same attitude to when exceptions should be used.

Furthermore, any mature language (indeed, any mature product) is like a blurred photograph of something in motion, or like an evolved animal. It contains strange vestigial limbs and other leftovers from its evolutionary past. And there is no reason to suppose that the evolution has stopped. This is a domain that somehow refuses to sit still, stop fidgeting and behave itself while we all attempt to understand it properly.

So even once you’ve grasped the importance of understanding exceptions properly in your choice of language (which many don’t), the next challenge is distilling that understanding from the fragments of good and bad advice, or mere clues, scattered around you.

So I’ll tackle this as a series of articles, and gradually turn this post into the contents page, plus part 1.

I’m going to start with something that seems incredibly simple and basic, but which many working programmers don’t agree on. Some people say exceptions represent errors (what kind of errors?) Others say they represent exceptional – that is, rare – conditions (how rare?) And yet others say they shouldn’t be used; they’re more trouble than they’re worth. All these people are wrong!

Exceptions provide a way for a routine to communicate a result; in that way they appear to compete with return values, and those who have a beef with exceptions often point to special return values as the obvious alternative. In reality they are very different, but related, things.

A function, in mathematical terms, is a way of getting a new value from an existing one. In programming languages, the “return value” of a function is how we communicate the value of a function back to the caller. This allows functions to be “composed”:

 y = f(g(h(x)));

So x is passed h, and the result of that is passed to g, the value of which is passed to f, the ultimate result being stored in y.

Suppose you write some code to figure out a person’s age. As a pure function, it would need two parameters, the current date and the person’s date of birth. We only need to pass in the person’s date of birth, because we can look at the clock (in other words, we’re not using a pure functional language). And given the purpose of the function, it is unquestionable that the return value should be the age of the person. That’s the whole point of return values. Let’s say it’s an integer measuring years, in the traditional way.

But suppose the date object happens to represent a time in the future. This is meaningless – we don’t talk about the age of someone who hasn’t been born yet. In that case our function should have no value at all – it should be undefined. This is a really important part of the concept of functions: that they have a domain of allowed inputs, outside of which they simply don’t have a value.

We could choose a so-called “magic” value to represent an undefined result. Let’s see, we’ve already said that negative ages don’t make sense, so we aren’t using the negative range of integers. So we can use -1 as our magic value that means “undefined”. Problem solved!

Well, sort of. But now the next piece of code in the chain will receive an age of -1, and will try to treat it as meaningful, because the coder didn’t take the trouble to check for the magic value. The default, the easiest thing to do, is to ignore the problem. This is a thoroughly rotten situation; it means that the side-effects of a bug are able to spread throughout the state of a program, causing mysterious symptoms that may be far removed from the original cause.

So exceptions reverse this situation. They allow us to write functions that have a well-defined domain, such that the default behaviour where the boundary of that domain is breached is for the program to stop running and display some kind of complaint, the correct interpretation of which is: “fix me, you idiot.”

In order to cope with the situation automatically, the programmer must explicitly “opt in” by handling the exception. This is a really important aspect of good design: choosing a sensible default. Making it so that when people aren’t paying attention, they happen to wander in a reasonable direction.

Even in C, which has no exceptions in the language, we nevertheless hope that dereferencing a NULL pointer will cause a “core dump” or “access violation” message and halt execution. So from that perspective, we can think of exceptions as allowing us to extend that kind of robust checking by default to other types besides pointers.

And it goes without saying that it is advantageous for a function with no value to be able to state this in a standard, unambigous way, instead of trying to encode the concept of “undefined” in some magic value, which would need to be different depending on the value’s representation.

Void Functions and State Changes

So we have a clear justification for exceptions where we have a return value. As we’re not talking about a pure functional language, what about functions that return void? These are functions that modify some state. But we can understand what an exception means here by thinking in pure functional terms and then translating back to imperative concepts.

Imagine that a void function accepts the current state of the entire program as a hidden parameter, and returns the new state as a hidden return value. This sounds comically inefficient, as we only need to keep the most recent state. But conceptually it’s the same thing, and it lets us use the same justification for exceptions: a function throws when it cannot produce any return value. Translating back to impure terminology, the void function was unable to completely finish modifying the program state. And so if we’re going to be really helpful in writing void functions, they better behave like the functional equivalents: if they can’t make the complete change, they ought to make no change at all.

Next time: If we’re really being rigorous, should we even be allowed to catch exceptions in our programs?

Categories: Uncategorized Tags:

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s

%d bloggers like this: