Apparently the fact that I use exceptions in my suggested control flow mechanism for collections makes me a bad person. This idiocy touches on a sore point for me. I have been invited to write a post on “Why exceptions for control flow are a good idea”. So. Here it is.
The thing is, I really can’t be bothered. I can give and have given useful use cases, but I’ll just be told that they’re evil because I use exceptions. So instead I will argue the converse: Why they are not a bad idea.
Let’s consider some common objections:
Exceptions are slow
No they’re not. Exceptions in C++ (and I believe .NET) may be slow, but many implementations of exceptions are in fact extremely fast. The various ML implementations have taken advantage of this for quite some time. The JVM too (with some mild tricks) has very fast exceptions.
Exceptions are for exceptional situations
This argument is pervasive. It’s also meaningless. Depending on the intent of the one claiming it, it is one or more of three different forms of fallacy:
- Assertion of the conclusion
- Argument by name. They’re called exceptions, therefore they must only be for exceptional conditions. If I called them kittens would it suddenly be ok to use them for control flow? (Next post: Kittens for control flow considered harmful. I look forward to seeing Josh Suereth’s rebuttal).
- Argument by meaningless blither. What distinguishes “exceptional conditions” from “control flow”? I have reached the end of a million element list. This happens one time in a million! That sounds pretty exceptional to me!
Writing exception safe code is hard
No it’s not. Be sure to clean up anything that could need cleaning up in a finally block. If you write decent code, and you’re using a language with automatic memory management so that this doesn’t apply to Joe Random Object, these tend to be small and relatively well isolated cases. Where possible you abstract this out entirely by using patterns like automatic resource blocks (remember: In decent languages it’s just a library).
Further, even if it were true, this would be an argument against using exceptions at all rather than using them for control flow: If you aren’t sure where exceptions may be thrown from (which is particularly true if you’re writing generic library code), you have to write exception safe code.
It’s confusing
Everything’s confusing when you first encounter it. Then you get used to it, you find where it does and doesn’t work, and then it becomes non confusing. As long as you continue to maintain that a technique is evil and should not be used it will remain confusing.
Debuggers will break on exceptions
Decent debuggers allow you to specify which exceptions you break on and which you don’t. It’s easy to provide a marker class that says “This exception is for control flow. Don’t worry about it”.
You might accidentally catch the exceptions you’re using for control flow with too broad exception handling
Consider the break/continue example. What happens if I do the following (and break is implemented with exceptions):
for(thing <- myThings){
try {
doStuff;
if(dieNow) break;
} catch { case (e : Exception) => e.printStackTrace }
}
We accidentally catch the break! This is bad.
There are a couple aspects to my response to this:
First off, given sensible library design it doesn’t happen at all. Control flow exceptions get moved outside the hierarchy of normal exceptions which it’s sensible to catch (in my quick hacked together version they were Errors. Really they should be subclasses of ControlFlow which extends Throwable).
Secondly, this is a problem with too broad exception handling: You’ve handled a generic exception where you probably just wanted to handle a specific one. This is always known to be bad.
Thirdly, this is a really really easy problem to spot. As soon as you even lightly test the code you will see a stack trace for Break being printed and will realise what you’ve done wrong. This might cost you a good 10 seconds of confusion tops. (If you simply swallow the exception silently this isn’t true. But if you do that I have no sympathy for you at all and your code deserves to break).
The problems with using exceptions for control flow are well documented
Actually, they’re not. If you don’t believe me, check google scholar.
Edit: It has been pointed out to me that if you add quotes to the search term then you do get a few hits. They all seem to be random Java best practices documents deriving from Rules for developing robust programs with java exceptions, which does not appear to present any arguments not already covered here. For completeness, here is the relevant section from it:
IM1: Do not use exceptions for control flow.
It is possible to use the exception handling mechanism for control flow also during normal
program flow. This makes the code harder to read, harder to analyze, significantly slower and
opens up for some horrible situations if an actual exception were to occur. Exception should,
as the name states, only be used in exceptional situations.
So we’ve got one count of “confusing”, one count of “performance”, one of what I can only imagine is a vague allusion to the problem of accidentally catching the exception and a chaser of “Exceptions should only be used in exceptional circumstances”. This, as far as I can tell, is the gold standard argument for the opposition in the Java realm.
Feel free to post additional arguments for or against in the comment. I’ll expand the article with any good ones posted.