2010-09-07

Exception madness

Before exceptions became main-stream technology in programming languages (about 1.5 decades, i think), error control in programs was a delicate matter.

The problem was due to a common design pitfall that plagued (and still plagues) many technologies. That pitfall is named: in-band signaling.

Note: the name "in-band signaling" comes from the telecommunications industry (see wikipedia entry). The term comes from the fact that when you dial a number, the number itself is sent as sound over the line (you hear the tones, isn't it?). In-band signaling doesn't seem to be a problem, until you realize that other functions of the telecommunication network work the same way. Thus by knowing the right code, you can just dial it on your phone and lo and behold, the next call is not billed and things like that. That used to happen a lot in the 70', 80' and early 90' because of the modem cost of communications. I think it doesn't happen much today because once you pay for Internet access, the world is right at your fingertips.

Just think about it. If you don't have the exception infrastructure, the exceptions must be then returned as a special form of result of your method or function.

In languages like C, it is very common to see things like:

FILE *file = NULL;
if ( (file = fopen("/some_file", "w")) != NULL) {
     /* oops */
     extern int errno;
};

Notice that the function fopen is supposed to return a file handler (of type FILE *), but if an error occurs, then NULL is returned and you have to check the errno variable to see what went wrong.

Do you see that the error is sent in-band with the result?

This caused an awful lot of problems with many programs. Even to the point of using the most obnoxious pair of library calls in the C environment (be it setjmp and longjmp).

What did this two calls provide? In plain english: the ability for a program to set(jmp) a recovery point were bad situations could be handled and the ability for that program to (long)jmp to that point when bad things happened. I have to say that you ought to be a terrorist to use these two function calls, so controlling for errors in-band infected regular programming as badly as a venereal decease.

Perhaps you recognize that jumping back to a place where you know how to handle errors is just a tiny part of what the exception infrastructure provides in modern programming languages, but there is a lot more than just the jump in exception handling. That lot more is what made the pair setjmp/longjmp almost impossible to use properly. These jumps actually worked by just instantly moving the execution to a previous program scope, whereas exceptions destroy intermediate scopes (meaning: cleaning up stack objects and giving a chance to destroy manually allocated objects for languages without Garbage Collection).

All of a sudden, with exceptions you can write much cleaner code. If, for instance, fopen would return an exception if the file cannot be opened, the following code will make a lot of sense:

try {
    FILE *file = fopen("/some_file", "w");
    // do something with the file
} catch (FileNotExistsException ex) {
    // handle error
};


However...

Bad things happen

In our case, 3 bad things to be exact.

The first bad thing is that the code to handle an exception gets separated from the code originating it. This actually increases complexity in our programs. Just consider our last example source code and imagine that the code implied by the line // do something with the file is actually 150 lines long (or even 15 lines long). Then, the line that generates the exception on the catch clause is not immediately obvious. In this case, the exception name will give a clue, but with more obscure exception names, the relation is not evident, just implicit and that augments complexity. You can of course put a specific try/catch pair for each call that can generate an exception, but that makes for bigger code. And think that we have not even considered the finally construct that is expected to be used to undo object creation side-effects. A comment stating the source instruction for each catch clause will help with this issue, but not solve it and is extra programmer effort.

This leads to the second bad thing. Given that properly writing exception handling code is an extra effort, many programmers started to do one of two things: 1) put a generic catch (Exception e) clause, or 2) just not handling exceptions at all. The first case just looses the chance for fine grained error handling. A single exception is completely abortive of the function or method.

The second case degenerates into the third bad thing, specially for web services or web applications.

Enter the third bad thing. Considering bad thing number one and bad thing number two, proper exception handling is complex, so when we have a number of people writing code, the only we can count on is exceptions will not be properly handled. But don't despair. We can put a catch (Exception e) at the highest levels of our app and, at the worst, use an all fucked up generic error page. Even better, we can use the exception class to select a proper message to display, so it works like charm.

Nothing particularly bad with that implementation, i even like it and use this idea, but only if it is not a replacement for proper error handling.

The real (unintended) problem with this approach is that, when uncontrolled, it fosters...

Exception Madness

Why is that? Well, when programmers can count on having a safety net below, they tend to get lazy (we tend to get lazy). Now, you can just throw exceptions for almost any code you don't want to write.

For example, let's say you have an input field in a form that needs to be even (divisible by two). Some programmers (sadly not a few) will write a utility function or method called even that receives a generic object or integer and returns a boolean (true) if the value is even, but throw an exception if the value is odd. Of course, if you consider the method name there is no good reason for it to throw anything other that InvalidParameterType if it accepts general objects as input; but if you are going to throw your stomach contents on to your caller, at least have the decency to call the method something like ThrowsIfNotOdd.

These kind of situations are doubly perverse. First because they abuse the last resort exception handling and second because they do not put business or application logic in the right place (if you are going to abuse, then the caller should throw, not the utility function).

Conclusion

Exceptions are actually a step ahead in error handling, a step i am glad was introduced to many programming languages; but i think the problem is that once again, we got the silver bullet syndrome with them. Exceptions are an excellent tool, but only to the extent they are used properly, and they have some side effects too that you should be aware of.

Enough for a very very very late third post. Sorry about that.