Home > Uncategorized > Exceptions Part 2: Why do we need to catch them?

Exceptions Part 2: Why do we need to catch them?

See the Contents Page for this series of articles

Last time we established that an exception is the way a function unambiguously produces no value – or no side-effect, which amounts to the same (no)thing. To take the best known example from mathematics, the function f(x) = 1/x has no value if x is zero. If you’re asking what anything divided by zero is, well… you shouldn’t be asking. Just don’t ask.

But by catching an exception, you are saying “I expect this”. So to be entirely rigorous about it, isn’t it wrong that we ever try to catch these things? Maybe, like in mathematics, we should never attempt to evaluate them in the first place.

Suppose we want to open a file. We want to avoid exceptions, so we first check if the file exists, and if it doesn’t we never try to open it. But that doesn’t cover the case where the user doesn’t have the right permissions. So we add another check for the permissions. And so on, until we’ve basically reproduced all the validation that is going to be done again anyway when we eventually attempt to open the file. Sounds like a waste of effort, and it is, but that’s not the worst of it.

The more fundamental problem goes by the terrifying name shared mutable state under concurrency. And this is just a fact of life that cannot be worked around, even if you don’t care about performance. You could do all those checks before you try to open the file, but then in that particular nanosecond another process could sneak in and delete the file, so all your checks were pointless. The answer you came up with, “we’re good to go”, was a lie, and in general must always be a lie. The file system is a mutable resource, shared across many separately processes, with no common approach to synchronization. This is a pretty good description of the state of the universe too. It means we cannot predict with certainty whether something will work, unless we completely and accurately model all other processes in the universe, which is clearly not practical. We can only attempt some operation and then we’ll know afterwards if it worked or not.

Now, I know what you’re thinking. Functional programming languages surely don’t have this problem. But in practice they do, because on closer examination they’re not so pure after all (see the bottom of this page). “A programing language ain’t no kinda programming language if he don’t know what time it is, bro”, as I believe Sir Charles Antony Richard Hoare once said. Useful programs have to act on things that they do not already have complete information about. Complete information is too expensive to wait for. It’s more cost effective to make a reasonable guess.

Even so, just because we can’t avoid sometimes expecting exceptions, that doesn’t mean we can pretend that we expect all exceptions. There should be many that we do not expect, and therefore should not catch.

Crashing Is Good

I’ve noticed that people by nature aren’t interested in the subject of unhandled exceptions. They tend to think like this:

An unhandled exception means my program is going to crash. So it’s already the worst possible situation, and so what do I care about the fine details of how it crashes? I have more important things to worry about than that!

But once you’ve spent a decade or so trying to support real software products “in the wild”, you get a different perspective. Exactly how a product crashes is hugely important. It needs to halt in an unambiguous way, not try to stagger on embarrassingly. And it needs to leave behind plenty of clear evidence. It should halt as soon as it detects an unexpected condition. Crashing, relatively speaking, is awesomely helpful. What is much worse is failing to crash when you should. A program that keeps running when it is an unknown state is a dangerous rogue agent.

The mistake is in thinking that losing what is in the memory of one process (which is what happens in a clean crash) is the worst that could possibly happen. How about losing what is in the file system, or the database? Bugs that trash persistent data are potentially far more destructive. And what about destroying the evidence needed to identify and fix the bug? That means the bug has to occur to real users hundreds of times before you are lucky and it happens to crashes in a helpful way. And so catching an exception and attempting to recover can be hundreds of times more disastrous than not catching the exception.

So now we’ve established the landscape of exceptions. In summary, so far:

  • We need exceptions because sometimes functions don’t have values (or cannot complete a state change)
  • We need to be able to catch some exceptions because real-world functions are often timing sensitive. We don’t control all their inputs, so we can’t predict when they will succeed.
  • We need to be able to not catch some exceptions because they tell us our program is in an unknown state and it’s time to quit before we can do more serious damage.

All very clear and rational. Now it’s time for the messy stuff.

Next time: Why do we need finally blocks?

Advertisements
Categories: Uncategorized Tags: ,
  1. Trillian
    June 24, 2010 at 11:48 am

    I love this series of articles. Although I felt like I somehow instinctively understood most of this, having it put into such precise words is helpful. I’d be surprised if I don’t get the opportunity to quote parts of this text on proper exception usage.

    I have one question for you though: in cases where race conditions are not possible, such as when manipulating fully owned data in a single thread, are there any justifications to catch an exception? Do you believe we should always do the necessary checks before attempting an operation that could otherwise throw?

    I’m eager to read part 3, I hope you’ll surprise me with something I wasn’t aware about finally blocks! 🙂

  2. earwicker
    June 24, 2010 at 1:31 pm

    Cool, thanks!

    Suppose you perform all the necessary checks (whatever they might be) to ensure that you don’t attempt an operation that might throw. You discover that there’s a very good chance it will throw. So you can’t do the operation you need to do. So the question is: what do you do instead?

    It’s pretty rare that you can simply do nothing. There must have been some reason why it was necessary to attempt the operation! And even if you do “nothing” in practise that means you need to tell the user that you couldn’t do it. And this only applies to the “outer layer” of an application, the part the user directly interacts with.

    So for the innards of an application, and certainly for a reusable library, you cannot make that kind of decision – it’s not your place to talk directly to the user! You have to leave it open. And so throwing an exception is the correct thing to do anyway. This allows the outer layers of an application to decide how to respond – they may not catch it at all.

    So there may be no advantage in checking to avoid an exception if all you’re going to do is throw another exception on the failed check.

    Even so, you can sometimes add clarity by doing your own check and throwing a better exception, more clear and specific.

    Hence the obvious case of checking for a null parameter and throwing ArgumentNullException. It’s likely that if the method continued executing it would very soon throw NullReferenceException anyway, but there’s a little bit of added value in throwing the name of the problematic parameter as soon as possible.

    This leaves the whole question of what types of exception should be allowed to escape from a given layer of an architecture, which I will have to visit in a later article! 🙂

  3. Marcel Doru Popescu
    June 25, 2010 at 4:05 am

    Daniel, many thanks – I’ve read both these two articles and the comments you left on Eric’s post, and I’ve learned a lot. I thought I knew more about exceptions than I actually did 🙂 – I was completely ignorant of the fact that there are Ugly exceptions. Thanks for ruining my next sleep 😛

    • earwicker
      June 28, 2010 at 1:10 pm

      @Marcel – I expect you did know about Ugly exceptions, e.g. NullReferenceException. The bit that is new to most people (I find) is the idea that finally blocks should NOT execute when an Ugly exception is tearing down the process. Some stuff about that in the newly-added part 3.

  1. June 28, 2010 at 1:06 pm
  2. January 29, 2012 at 1:21 pm

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: