<p><pre><code> > 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!
</code></pre>
Unless you were expecting the list to have infinite length, then this would be a misuse of exceptions. The true logical fallacy here is the author of this post's argument from personal incredulity. The fact that he hasn't yet learned how to distinguish between exceptional conditions and control flow doesn't mean that it's an impossible or meaningless distinction.
I agree with you. There are way too many cases of people making generalized arguments "you shouldn't do XYZ in programming because it's slow/confusing/etc" that really just have no place being uttered unless they can show mathematical proof it is too much to bother with in <i>every</i> situation. Back in the 80s and early 90s it was "no compiled language should ever be used for critical software", and then in the late 90s and early aughts it was "no VM'd language". Now what? It's all there is! You'd be nuts to NOT use a VM'd language. And the only reason we know any better is because people ignored the received wisdom and just did their own thing.<p>Keep doing your own thing.
<i>Writing exception safe code is hard<p>No it’s not. Be sure to clean up anything that could need cleaning up in a finally block.</i><p>This is like saying... <i>be sure to never copy more bytes than the buffer capacity</i>. Easier said than done.<p>Writing exception safe code is very hard. Do not take my world for it. Read Alessandro Warth's paper (with Alan Kay as a co-author) [1]. Do not skip section 3...<p>Let me quote section 3.1:<p><i>In languages that support exception-handling mechanisms (e.g., the try/catch statement), a piece of code is said to be exception-safe if it guarantees not to leave the
program in an inconsistent state when an exception is thrown. Writing exception-safe
code is a tall order, as we illustrate with the following example:</i><p><pre><code> try {
for (var idx = 0; idx < xs.length; idx++)
xs[idx].update();
} catch (e) {
// ...
}
</code></pre>
<i>Our intent is to update every element of xs, an array. The problem is that if one of
the calls to update throws an exception, some (but not all) of xs’ elements will have
been updated. So in the catch block, the program should restore xs to its previous
consistent state, in which none of its elements was updated.
One way to do this might be to make a copy of every element of the array before
entering the loop, and in the catch block, restore the successfully-updated elements to
their previous state. In general, however, this is not sufficient since update may also
have modified global variables and other objects on the heap. Writing truly exceptionsafe code is difficult and error-prone.</i><p>Now, I have seen a lot of code, and very very very few times I've seen someone restoring the state of a collection after an exception blows.<p>[1] <a href="http://www.vpri.org/pdf/tr2011001_final_worlds.pdf" rel="nofollow">http://www.vpri.org/pdf/tr2011001_final_worlds.pdf</a>
I think this argument is generally taken out of context; my biggest concern would indeed be that using exceptions for control flow is non-idiomatic <i>currently</i> for most mainstream languages (and yes, Python disagrees).<p>This means that going against the grain will cost you time (and not you necessarily, but your company / colleagues, etc), and that time better come with some great benefits for it to be worthwhile.<p>So really, the argument can be looked at from many angles, depending on what value system you are using / what you would like to optimise for. I like to optimise for least surprises / development time.
I've used exceptions for flow control in validation before. Worked well.<p>Basically, you have nested validation code, and the second you hit something which invalidates your data you throw an InvalidDataException(X), catch it at the top of the validation, and then report back to the user that their input is broken because of X.<p>The alternative was that every method would need to pass back whether it had found a validation error, and every place that called one would need to check that returned value. Huge numbers of lines of code, for no real gain.<p>(Obviously, this doesn't work if you want to return _all_ the things that are wrong with the data.)
I think it all depends on context. I use checked exceptions for handling edge cases in Java, which turns something like this:<p><pre><code> public Session startByInterviewId(Long interviewId, String email, String name) {
Session session;
Interview interview = interviewService.getPublicById(interviewId);
if (interview != null) {
Account user = userService.createUser(name, email, interview.getLocale());
if (user != null) {
session = startByInterview(interview, user);
}
}
return session;
}
</code></pre>
into this:<p><pre><code> public Session startByInterviewId(Long interviewId, String email, String name)
throws Interview.NotActive, Interview.NotPublic, Interview.DoesNotExist, Account.EmailExists {
Interview interview = interviewService.getPublicById(interviewId);
Account user = userService.createUser(name, email, interview.getLocale());
return startByInterview(interview, user);
}
</code></pre>
in which case I'm very much in favor of using exceptions as control flow mechanisms.<p>Instead of just getting nulls to indicate failure I now even know exactly what caused my error.<p>I would never even think of writing code like this in JavaScript or Clojure though and I'd guess is that in Scala/Haskell a custom Option would be much better.
Discussions about performance are statements of fact and can be measured. A jsperf shows that code executing in a try catch for chrome on a mac can be up to 3% slower for me[1]. Let's just get the numbers and show them and if they're valid they're valid, if not let's just dispel the rumors. Arguments about code architecture or best practices for intangible reasons are bikesheds. Just do what you like and be consistent and that's good enough. So if you can't demonstrate slowness, can't demonstrate a real problem as in the code wont work, then there is no real argument. In JavaScript avoid exceptions as control flow though, because they really are slower.<p><a href="http://jsperf.com/try-catch-error-perf/3" rel="nofollow">http://jsperf.com/try-catch-error-perf/3</a>
I'm reminded of Kent Pitman's essay "Condition Handling in the Lisp Language Family" (<a href="http://www.nhplace.com/kent/Papers/Condition-Handling-2001.html" rel="nofollow">http://www.nhplace.com/kent/Papers/Condition-Handling-2001.h...</a>):<p><i>To properly understand condition handling, it is critical to understand that it is primarily about protocol, rather than mere computational ability. The establishment of protocols is a sort of before-the-fact hedge against the "prisoner's dilemma"; that is, it creates an obvious way for two people who are not directly communicating to structure independently developed code so that it works in a manner that remains coherent when such code is later combined.</i>
"Debuggers will break on exceptions... Decent debuggers allow you to specify which exceptions you break on and which you don’t."<p>This drives me mad in Visual Studio, if you specify a set of exceptions to break on, then later decide you need to catch all exceptions, there's no easy way to go back to the first set.<p>I'm surprised this question and partial answer on Stack Overflow haven't got more votes:<p><a href="http://stackoverflow.com/questions/5452480/how-to-save-and-manage-debug-exceptions-preferences-in-vs2010" rel="nofollow">http://stackoverflow.com/questions/5452480/how-to-save-and-m...</a>
I was going to do a post rebuking the points made, but rather unsurprisingly it seems plenty of others already have done so, in a manner much less arrogant than OP's.<p><a href="http://www.javaspecialists.eu/archive/Issue187.html" rel="nofollow">http://www.javaspecialists.eu/archive/Issue187.html</a> and <a href="http://onjava.com/pub/a/onjava/2003/11/19/exceptions.html?page=2" rel="nofollow">http://onjava.com/pub/a/onjava/2003/11/19/exceptions.html?pa...</a> are two well written sources of many that Google turned up on the subject.
The author notably tries to refute arguments against the use of exceptions for control flow, but doesn't present any reasons (at least not in this article) as to why this usage might be a <i>good</i> idea.<p>There's no compelling reason to introduce odd, non-idiomatic code patterns when they don't confer a significant benefit. Some people here argue that the "intent" of exceptions in a language design is not relevant compared to what <i>can</i> be done with them. In general, I disagree with this. Language designers' intent informs the expectations of readers of code, and your readers' expectations are important. Code is a form of writing for communications, both with the computer and with maintainers; it's not artistic writing, where you might deliberately violate expectations or norms in order to delight or surprise your audience.<p>That said, using exceptions as a method of transferring control to a non-local, dynamically-bound location is a powerful technique for which few languages offer an explicit alternative. It's also difficult for readers to analyze, so it should be used sparingly. For instance, I can think of reasons that one might use this technique within a container or algorithm library, but I can't think of a good reason for a container library to force this control flow idiom on callers by throwing exceptions to signal common situations.<p>(As usual, Common Lisp is unusual here. It has a throw/catch feature which is explicitly a flow control mechanism, and not just an error-handling mechanism. The standard documentation is interesting in that the Notes section suggests exactly <i>when</i> one would want to use this mechanism:
<a href="http://www.lispworks.com/documentation/HyperSpec/Body/s_throw.htm" rel="nofollow">http://www.lispworks.com/documentation/HyperSpec/Body/s_thro...</a>)
I couldn’t agree more and have long been arguing that we should use exceptions when they are a useful tool for the job because their semantics do what we need at the time. Sometimes that means exiting early because you can’t do what you’ve been asked to for some reason. Other times it means exiting early because you’ve already done everything you were asked to and there is no point in continuing further.<p>The main thing I would add to the original article is that the author is being a little kind to the critics on some of those points. For example, not only do exceptions not inherently need to be slow, they can actually be <i>faster</i> than the “classical” equivalent via return codes and the like. This is essentially because any jump-table-based exception mechanism means the non-exceptional control path can omit all the test logic that would serve only to exit the current block early. That could both reduce the size of the non-exceptional code and reduce the number of potential branches, each of which can be helpful for optimization. Although it’s rather less significant in performance terms because it only tends to happen once, when an exception does occur you also don’t have to run through all the individual levels of unwinding the stack with a jump table approach, as you can jump straight to levels that actually need to do something on their way to handling the exception and skip over anything that was just going to return immediately anyway.<p>As another minor data point, I haven’t found exceptions in C++ to be slow. Most/all of the major compilers seemed to have moved to a table-driven approach last time I used a broad set of them. The nasty overhead in C++ is more likely to be in the size of the generated executables; I’ve seen a compilation with exceptions disabled shave nearly 1/3 off the executable size.<p>Of course exceptions have somewhat different meanings and can have very different implementation overheads in different languages, so I’m not arguing that all advice to use them sparingly is bad. As always, it’s important to separate the dogma from the rational arguments.
Back in my early years as a software developer I once though that I could make some really eligent code by using exceptions for control flow. I used it to do a form of what I now know is pattern matching. It really cleaned up the code and made it super easy to extend and read, but it was a _terriable_ idea. It was slow and since nothing was checked by the compiler I found myself often at my root catch block trying to figure out how my nested catches didn't work.<p>Exceptions are not suitable for control flow. Yes they work and can sometimes give nice features, but typically they are just showing that you have poorly designed code and should be doing something differently.<p>'/s/Exceptions/GOTO/g' and you have essentially the same argument
Exceptions break normal encapsulation mechanisms but as long as you code is exception safe, nobody cares, it's just that exceptions are the new goto, and nobody likes goto.<p>However they are only few good reasons to use goto, and exceptions for control flow should be treated the same.<p>Therefore the point we should make against exceptions for control flow is not exceptions are for exceptional
circunstances but; as a rule of thumb exceptions are for errors. And indeed they help a lot in not obscuring
normal program logic, we allready have other tools for control flow.<p>Nobody likes goto (and my bad english)<p>bye
It entirely depends on the language as to the level of acceptability of using exceptions for control flow. In Java, it's <i>moderately</i> routine to get a random exception or two that you recover from and go along your way merrily. Python similarly is A-OK much of the time.<p>In Objective C, exceptions are "he's dead Jim" territory.<p>Some of the differences of use are caused by the implementation of exceptions in those environments and the expectations programmers of libraries in those environments to what happens when exceptions occur.
Yeah, sometimes, using exception as control flow is the only simple and clear way. I do use it occasionally.<p>ex. Lets assume that a problem can be attacked as algorithm1 (A1), and algorithm2 (A2). A1 is fast but cannot handle some corner cases while A2 is slow and can handle all cases. we also assume that there is no easy way to tell whether A1 is good or not without invoking A1.<p>So the function can be implemented as:<p>void A()<p>{<p><pre><code> try {
A1();
}
catch (e) {
A2();
}
</code></pre>
}<p>void A1()<p>{<p><pre><code> .....
if(corner case) raise();
.....
</code></pre>
}<p>Is there a simple way to avoid using eceptions here?
The reason why using exceptions for control flow is bad is because it
violates the Samurai Principle. Now why do I refer to a cheesy made up
principle instead of explaining what I mean? Why do people use
"exceptions are for exceptional situations" and similar empty
expressions?<p>Because programming is just as much about <i>communicating intent</i> as it
is about writing efficient code! Following guidelines and maxims is
extremely useful because if both the writer and the reader understands
the protocl, then communication is smoother.<p>The "Exceptions are for exceptional circumstances" protocol means that
the reader of the code doesn't have to wonder "Is this a control flow
situation? Is this exception supposed to be handled? Where does the
code resume?"<p>Following the Samurai Principle
(<a href="http://c2.com/cgi/wiki?SamuraiPrinciple" rel="nofollow">http://c2.com/cgi/wiki?SamuraiPrinciple</a>) similarily eases the
cognitive load of the person reading the code. It allows you to treat
each callable piece of code as an isolated unit whose value is equal
to what it returns. For non-critical code, you can completely forget
about exception handling because you dont need to bother. However, if
an exception is thrown you can be equally assured that something
unexpected happened. It was to hard for the "samurai function" to
handle. Simple and extremely convenient.<p>One example of not following the Samurai Principle or "exceptions for
exceptional situations" happened at my last job. We were an mobile
payment processor and trying to issue a charge to a customer when
their balance was to low would result in a
BalanceToLowException. Except that wasn't anything out of the
ordinary, customers would often not have enough funds! The situation
would be handled by retrying the transaction some other day. The API
for charging customers would better have been designed returning a two
tuple (chargeStatus,errorMsg) so that the following code could
continue in the same location whether the charge succeeded or
not. Throwing exceptions for ordinary events lead to spaghetti code.<p>You can see similar misdesigns in some database ORM:s in which some
queries throw silly NoRecordsFoundException which user code is forced
to handle. Or Python's classic<p><pre><code> try: return int(somestr)
except ValueError: return None
</code></pre>
Often the string you're passing to int() is user input so you <i>expect</i>
it to often be non-numeric.
Oleg Kiselyov uses exceptions for control flow in his Delimcc library for Ocaml, and if that doesn't establish that doing so is perfectly acceptable, I don't know what could.