Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Back in the late 90's I worked quite a bit with a code base that used exceptions as flow control. It implemented a finite state machine to drive telephony applications. Lately I am reminded of those days because I've been doing MVC web apps.

They both have Controllers that decide where to go next and supply the data to that destination logic. User actions from the domain of an old-school telephone, like DTMF tones, became parameters to action methods, but instead of returning something like a ViewResult, they threw a StateTransitionException.

I think the main difference was that action methods were void functions. I don't remember all the things I did with this fact but I've been hesitant to even go down the road of remembering because since that job, like 15 years ago, I never saw this in production code at any other job. I assumed this was a sign that it was a so-called anti-pattern.

Is this the case, and if why?

Update: when I asked the question, I already had @MasonWheeler's answer in mind so I went with the answer that added to my knowledge the most.. I think his is a sound answer as well.

share|improve this question
1  
Related question: programmers.stackexchange.com/questions/107723 –  Eric King Mar 4 '13 at 22:33
3  
No. In Python, using exceptions as control flow is considered "pythonic". –  user16764 Mar 5 '13 at 1:37
    
I suspect you're being ironic or bitter. I appreciate both. Just keep your damned dirty empty catches out of the JavaScript. –  Erik Reppen Mar 5 '13 at 3:43
    
If I were to do such a thing I Java, I certainly would not throw an exception for it. I would derive from some non-Exception, non-Error, Throwable hierarchy. –  Thomas Eding Aug 15 at 14:58

7 Answers 7

up vote 16 down vote accepted

There's a detailed discussion of this on Ward's Wiki. Generally, the use exceptions for control flow is an anti-pattern, with notable situation and language specific cough exceptions cough.

As a quick summary for why, generally, it's an anti-pattern:

  • Exceptions are, in essence, sophisticated GOTO statements
  • Programming with exceptions therefore leads to more difficult to read, and understand code
  • Most languages have existing control structures designed to solve your problems without the use of exceptions
  • Arguments for efficiency tend to be moot for modern compilers, which tend to optimize with the assumption that exceptions are not used for control flow.

Read the discussion at Ward's wiki for much more in-depth information.

share|improve this answer
5  
Your answer makes it sound as though exceptions are bad for all circumstances, while the quesion is focused on exceptions as flow control. –  whatsisname Mar 4 '13 at 22:46
6  
@MasonWheeler The difference is that for/while loops contain their flow control changes clearly and make the code easy to read. If you see a for statement in your code, you don't have to try to figure out which file contains the end of the loop. Goto's aren't bad because some god said they were, they are bad simply because they are harder to follow than the looping constructs. Exceptions are similar, not impossible but hard enough that they can confuse things. –  Bill K Mar 5 '13 at 0:02
9  
@BillK, then argue that, and don't make over simplistic statements about how exceptions are gotos. –  Winston Ewert Mar 5 '13 at 0:14
2  
+1. for Exceptions are Goto's –  mattnz Mar 5 '13 at 4:05
3  
Okay but seriously, what is up with server-side and app devs burying errors with empty catch statements in the JavaScript? It's a nasty phenomon that has cost me a lot of time and I don't know how to ask without ranting. Errors are your friend. –  Erik Reppen Mar 5 '13 at 4:43

The use case that exceptions were designed for is "I just encountered a situation that I cannot deal with properly at this point, because I don't have enough context to handle it, but the routine that called me (or something further up the call stack) ought to know how to handle it."

The secondary use case is "I just encountered a serious error, and right now getting out of this control flow to prevent data corruption or other damage is more important than trying to continue onward."

If you're not using exceptions for one of these two reasons, there's probably a better way to do it.

share|improve this answer
1  
This doesn't answer the question though. What they were designed for is irrelevant; the only thing that is relevant is why using them for control flow is bad which is a topic you didn't touch. As an example, C++ templates were designed for one thing but are perfectly fine to be used for metaprogramming, an use the designers never anticipated of. –  Andreas Bonini Mar 5 '13 at 1:26
    
@Krelp: The designers never anticipated a lot of things, such as ending up with a Turing-complete template system by accident! C++ templates are hardly a good example to use here. –  Mason Wheeler Mar 5 '13 at 2:33
3  
@Krelp - C++ templates are NOT 'perfectly fine' for metaprogramming. They are a nightmare until you get them right, and then they tend toward write-only code if you aren't a template-genius. You might want to pick a better example. –  Michael Kohne Mar 5 '13 at 2:39

Exceptions are as powerful as Continuations and GOTO. They are a universal control flow construct.

In some languages, they are the only universal control flow construct. JavaScript, for example, has neither Continuations nor GOTO, it doesn't even have Proper Tail Calls. So, if you want to implement sophisticated control flow in JavaScript, you have to use Exceptions.

The Microsoft Volta project was a (now discontinued) research project to compile arbitrary .NET code to JavaScript. .NET has Exceptions whose semantics don't exactly map to JavaScript's, but more importantly, it has Threads, and you have to map those somehow to JavaScript. Volta did this by implementing Volta Continuations using JavaScript Exceptions and then implement all .NET control flow constructs in terms of Volta Continuations. They had to use Exceptions as control flow, because there is no other control flow construct powerful enough.

You mentioned State Machines. SMs are trivial to implement with Proper Tail Calls: every state is a subroutine, every state transition is a subroutine call. SMs can also easily be implemented with GOTO or Coroutines or Continuations. However, Java doesn't have any of those four, but it does have Exceptions. So, it is perfectly acceptable to use those as control flow. (Well, actually, the correct choice would probably be to use a language with the proper control flow construct, but sometimes you may be stuck with Java.)

share|improve this answer
    
I believe JavaScript actually does have continuations, starting in version 1.7 (see developer.mozilla.org/en-US/docs/JavaScript/Guide/…); but since no one actually writes JavaScript proper, I'm not sure what that's useful for. :-P –  ruakh Mar 5 '13 at 1:43
    
@ruakh: Aren't those Coroutines rather than Continuations? (Of course, it's highly likely that they are actually implemented with Continuations. Note also that they actually use Exceptions as flow control in the form of a StopIteration exception just like Python from where this feature was taken.) Anyway, the Volta project was several years ago and intended to work in every browser, so they couldn't rely on anything more than the cross-browser subset of ECMAScript 3.1. –  Jörg W Mittag Mar 5 '13 at 3:39
1  
If only we'd had proper tail-call recursion, maybe we could have dominated client-side web development with JavaScript and then followed up by spreading to the server-side and to mobile as the ultimate pan-platform solution like wildfire. Alas, but it was not so. Damn our insufficient first-class functions, closures and event-driven paradigm. What we really needed was the real thing. –  Erik Reppen Mar 5 '13 at 3:50
1  
@ErikReppen: I realize that you're just being sarcastic, but . . . I really don't think the fact that we "have dominated client-side web development with JavaScript" has anything to do with the language's features. It has a monopoly in that market, so has been able to get away with a lot of problems that cannot be written off with sarcasm. –  ruakh Mar 5 '13 at 6:51
    
Tail recursion would've been a nice bonus (they're deprecating function features to have it in the future) but yes, I would say it won out against VB, Flash, Applets, etc... for feature-related reasons. If it didn't reduce complexity and normalize as handily as it does it would have had real competition at some point. I'm currently running Node.js to handle the rewriting of 21 config files for a hideous C# + Java stack and I know I'm not the only one out there doing that. It's very good at what it's good at. –  Erik Reppen Mar 5 '13 at 14:33

It's completely possible to handle error conditions without the use of exceptions. Some languages, most notably C, don't even have exceptions, and people still manage to create quite complex applications with it. The reason exceptions are useful is they allow you to succinctly specify two essentially independent control flows in the same code: one if an error occurs and one if it doesn't. Without them, you end up with code all over the place that looks like this:

status = getValue(&inout);
if (status < 0)
{
    logError("message");
    return status;
}

doSomething(*inout);

Or equivalent in your language, like returning a tuple with one value as an error status, etc. Often people who point out how "expensive" exception handling is, neglect all the extra if statements like above that you are required to add if you don't use exceptions.

While this pattern happens to occur most often when handling errors or other "exceptional conditions," in my opinion if you start seeing boilerplate code like this in other circumstances, you have a pretty good argument for using exceptions. Depending on the situation and implementation, I can see exceptions being used validly in a state machine, because you have two orthogonal control flows: one that's changing the state and one for the events that occur within the states.

However, those situations are rare, and if you're going to make an exception (pun intended) to the rule, you had better be prepared to show its superiority to other solutions. A deviation without such justification is rightly called an anti-pattern.

share|improve this answer
    
As long as those if statements only fail on 'exceptional' circumstances the branch prediction logic of modern CPUs make their cost negligible. This is one place where macros can actually help, as long as you're careful and don't try to do too much in them. –  James Mar 5 '13 at 0:25

In Python, exceptions are used for generator and iteration termination. Python has very efficient try/except blocks, but actually raising an exception has some overhead.

Due to lack of multi-level breaks or an goto statement in Python, I have at times used exceptions:

class GOTO(Exception):
  pass

try:
  # Do lots of stuff
  # in here with multiple exit points
  # each exit point does a "raise GOTO()"
except GOTO:
  pass
except Exception as e:
  #display error
share|improve this answer
3  
If you have to terminate calculation somewhere in a deeply nested statement and go to some common continuation code, this particular path of execution can very probably be factored out as a function, with return in place of goto. –  9000 Mar 5 '13 at 0:40
1  
@9000 I was about to comment exactly the same thing ... Keep try: blocks to 1 or 2 lines please, never try: # Do lots of stuff. –  wim Mar 5 '13 at 3:57
    
@9000, in some cases, sure. But then you lose access to local variables, and you move your code yet-another-place, when the process is a coherent, linear process. –  gahooa Mar 14 '13 at 5:45
1  
@gahooa: I used to think like you. It was a sign of poor structure of my code. When I put more thought in it, I noticed that local contexts can be untangled and the whole mess made into short functions with few parameters, few lines of code, and very precise meaning. I never looked back. –  9000 Mar 14 '13 at 5:52

Using exceptions for control flow is generally considered an anti-pattern, but there are exceptions (no pun intended).

It has been said a thousand times, that exceptions are meant for exceptional conditions. A broken database connection is an exceptional condition. A user entering letters in an input field that should only allow numbers is not.

A bug in your software that causes a function to be called with illegal arguments, e.g. null where not allows, is an exceptional condition.

By using exceptions for something that is not exceptional, you are using inappropriate abstractions for the problem you are trying to solve.

But there can also be a performance penalty. Some languages have more or less efficient exception handling implementation, so if your language of choice does not have efficient exception handling, it can be very costly, performance-wise*.

But other languages, for example Ruby, have an exception-like syntax for control flow. Exceptional situations are handled by the raise/rescue operators. But you can use throw/catch for exception-like control flow constructs**.

So, although exceptions are generally not used for control flow, your language of choice may have other idioms.

* En example of a performance costly use of exceptions: I was once set to optimize a poorly performing ASP.NET Web Form application. It turned out, that the rendering of a large table was calling int.Parse() on approx. a thousand empty strings on an average page, resulting in approx. a thousand exceptions being handled. By replacing the code with int.TryParse() I shaved off one second! For every single page request!

** This can be very confusing for a programmer coming to Ruby from other languages, as both throw and catch are keywords associated with exceptions in many other languages.

share|improve this answer

Let's sketch such an Exception usage:

The algorithm searches recursively till something is found. So comming back from recursion one has to check the result for being found, and return then, otherwise continue. And that repeatedly comming back from some recursion depth.

Besides needing an extra boolean found (to be packed in a class, where otherwise maybe only an int would have been returned), and for the recursion depth the same postlude happens.

Such an unwinding of a call stack is just what an exception is for. So it seems to me a non-goto-like, more immediate and appropriate means of coding. Not needed, rare usage, maybe bad style, but to-the-point. Comparable with the Prolog cut operation.

share|improve this answer
    
Hmm, is the downvoting for the indeed contrary position, or for insufficient or short argumentation? I am really curious. –  Joop Eggen yesterday

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.