Friday, 23 November 2007

The sorry state of signalling conditions in "modern" languages

You know the mantra: "Only throw an exception in exceptional circumstances." A close brother of this mantra is: "Exceptions as flow-control are bad."

I submit to you, the single reader of this article: Exceptions as implemented by most languages are the solution to the problem: "Something bad happened! OMG! <commence-running-around-like-headless-chicken>." The problem should really be stated as: "Something bad happened! Is there anyone who can tell me what to do? If not, I'll just launch the debugger and wait for someone." It is a subtle but important difference.

Consider enabling some code to exit early because you want to have a quick test of the current parameters for validation. In C++, you might write:


int calc_value(int a)
{
parameters = setup_parameters();
int b = do_expensive_calc(parameters);
return b+a;
}
int do_expensive_calc(parameters p)
{
for(size_t i = 0; i < some_large_number; ++i)
{
lots_of_code();
if(i==1 && want_to_exit_early)
{
throw ReachedFirstIteration(calculated_value);
}
}
return calculated_value;
}

Oops! Except you can't do that can you? In a code review, inevitably someone is going to say: "HEY MAN NO EXCEPTIONS AS FLOW CONTROL. IT SAYS RIGHT HERE IN THE CODING STANDARD 1.9! (I'm so telling the boss)" And they are absolutely right. Well, except the snitching. We don't like those people. They aren't TEAM PLAYERS.

As you are new to the team, you go back and rewrite your code so you are no longer throwing the exception:

bool do_expensive_calc(parameters p, int & outval)
{
for(size_t i = 0; i < some_large_number; ++i)
{
lots_of_code();
if(i==1 && want_to_exit_early)
{
outval = calculated_value
return true;
}
}
outval=calculated_value;
return false;
}

Ugh. Mr. (or even Dr.) Senior Developer says: "Hey, thats the way this is done in C++. Whaddayagonnado?" and gives your code approval.

Mr. Senior Developer is right. But for the wrong reason. C++ compilers have been heavily, heavily optimized to have no overhead in non-exceptional circumstances. See this Going Deep episode on C++ exceptions for an understanding of how that is accomplished. Interesting stuff. But damnit, what about non-exceptional circumstances? And damnit, what about when I may not decide to take any action and want the code to keep going when this condition occurs?! So as you can see, he is right only because there is a big gaping deficiency in the C++ language and trying to patch the hole either gives you a performance issue or it makes the code ugly.

So hopefully you see now why the problem was restated. But that form still doesn't apply here. What we really want to say is: "Hey, I'm in some state, does anyone care? I'm just going to keep going otherwise." This is exactly what Lisp provides with the condition system.

The above example is a bit contrived. But what do you expect for a half hour of writing? See Chapter 19 - Beyond Exception Handling: Conditions and Restarts to get a very good example of when it is useful (and from a better writer.)

You can also see the video version of Practical Common Lisp if that appeals to you. Peter starts talking about conditions around 42 minutes into it.

Go forth and Lithp!

No comments: