Hacker News new | past | comments | ask | show | jobs | submit login

Since this is the internet, I would like to nitpick and point out that Dijkstra never said that gotos are always evil. From his considered harmful letter:

> In [2] Guiseppe Jacopini seems to have proved the (logical) superfluousness of the go to statement. The exercise to translate an arbitrary flow diagram more or less mechanically into a jump-less one, however, is not to be recommended. Then the resulting flow diagram cannot be expected to be more transparent than the original one.

The important thing is to make sure that the state of the program can be modeled by something as static as possible and as close to the source code as possible. Ideally you should be able to tell what is going on just by looking at what line of code you are at and you want to avoid those times where the only way to understand a program is to go back to "main" and keep track of the whole code path that was taken to reach the point you are currently at.

So a state machine with gotos is fine since the state machine states are strongly correlated to the source lines of code. On the other hand, an unstructured program translated to a goto-less one via the introduction of lots of "flag" variables is just as hard to understand as the version using gotos.

---

By the way, the "GOTO considered harmful" letter is smaller than your average blogpost amd is very easy to understand. I highly recommend that everyone should go ahead and read it if you haven't done so already :)

http://www.u.arizona.edu/~rubinson/copyright_violations/Go_T...




> Since this is the internet, I would like to nitpick and point out that Dijkstra never said that gotos are always evil.

To further nitpick your nitpick: I never claimed Dijkstra did say that gotos are always evil. I only said that he cried foul on goto, which he did:

> More recently I discovered why the use of the go to statement has such disastrous effects, and I became convinced that the go to statement should be abolished from all "higher level" programming languages (i.e. everything except, perhaps, plain machine code).

As an aside, the Jacopini paper [1] doesn't really seem to have anything to do with eliminating jumps. In fact, all of their example normalizations given in figures 20-22 clearly contain jumps.

[1]: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.119...

Edit: After further reading the Jacopini paper, I think I see what Dijkstra was hinting at: abstracting the jumps into 2 basic operations which are less powerful than a generic jump -- a conditional jump and an iterative jump. Jumps remain in the normalizations but are more restricted, and in some sense "abstracted away". However, the paper does explicitly say that not all flow diagrams can be decomposed this way.


I suspect that the programming landscape has changed so much that the goto debate of the 70s is no longer relevant. People used to use gotos because they were necessary; naturally, bad code got written; a theory emerged that goto caused bad code. Fast forward to the present. Gotos are so feared that most programmers have never even seen one. Naturally, bad code still gets written. What we now know for absolute certain is that it's possible to write any kind of code without gotos (including awful code). If you wanted to revive goto today, you'd have to force people to use it, because no one would know what to do with it. There's a goto-free solution for everything. Bad code would still get written, but I doubt it would look like the bad code of the 70s.


On the other hand, an unstructured program translated to a goto-less one via the introduction of lots of "flag" variables is just as hard to understand as the version using gotos.

Thanks for this (also to the parent). I've just realised that goto has been so purged from my consciousness that my code is full of the 'flag' pattern. I haven't even considered goto as part of any language I've used in the last 20+ years. I guess I have some assumption-questioning to do.


Replacing flags with break or return statements can often be a good way of making things a bit clearer (of course, this is a bit subjective). As a rule of thumb, gotos that jump forward are not as bad as gotos that jump backwards.


You can get the same effect as goto without using the dreaded goto statement by combining a while loop and a large switch. Instead of 'goto nextstate', you just assign the state to the variable that controls the switch and fall through the loop one more time. A switch is basically syntactic sugar for a jump table.* This seems to be a fairly standard pattern when implementing, say, a virtual machine.

* Of course, with optimization, the switch may turn into something completely different.


ufo had already mentioned this technique a couple levels up. Though it's equivalent, I think it's worse than just using gotos.

Suddenly you have extra mutable state to worry about: the "next" state and a loop control variable. At that point, you've removed a layer of abstraction and introduced complexity. You've said "let the next state be foo, then repeat this loop, then if the next state is foo, then enter state foo" when you really meant: "go to state foo".

goto in this case is practically declarative, insofar as a state machine diagram is declarative -- code structured in this way is a one-dimensional representation of the diagram.


Though it's equivalent, I think it's worse than just using gotos.

I'm not sure I could judge what it would look like just using gotos, because I've never seen it that way. I think in virtual machine implementations, this may actually be the most rational structure for solving the problem at hand.

I agree with your general point, though. When you remove something essential from a programming language, you are forced to reinvent it; actually, more complex version(s) of it.


Using gotos, it would look something like this:

    state1:
      /* do some stuff */
      if (/* something or other */)
        goto state2;
      else
        goto state3;
    state2:
      /* do some stuff */
      goto state1;
    state3:
       ...
The labels represent states, and the gotos represent transitions.

You're right that this isn't particularly good for implementing virtual machines, but for more "fixed" types of state machines this can be a pretty clear way of representing them in code. Mike Pall wrote a good overview of various virtual machine techniques here: http://lua-users.org/lists/lua-l/2011-02/msg00742.html (while he knocks the performance of the switch-based approach, I'd like to note that it's the most portable).

> When you remove something essential from a programming language, you are forced to reinvent it; actually, more complex version(s) of it.

That's a good way of phrasing it. It almost sounds like a lemma to Greenspun's Tenth Rule :)


A switch-based state machine can be as simple as something like this:

    enum { STATE0, STATE1, STATE2, STATE3 } state = STATE0;
    while(1) {
        switch(state) {
            case STATE0:
                if(input1) {
                    state = STATE1;
                } else if(input2) {
                    state = STATE2;
                }
                break;

            case STATE1:
                // etc.
        }
    }


break statements are goto statements with implicit labels! To really get rid of gotos, you'll need to have a loop control variable:

  ...
  int done = 0;
  while (!done) {
    ...
    done = 1;
    ...
  }
Yes, it works fine and it's not terribly compelex. The version with goto is simpler because it's a more direct translation of the corresponding diagram.


Aren't loops and ifs goto statements with implicit labels?


Heh, I suppose you're right :)


If you want to find all the implicit gotos, just disassemble the compiled code. Your program will be littered with jmp instructions and the conditional versions of jmp. There's no getting away from them.


Yup, I know. I just got a little carried away :)

My main point was similar to something you'd said earlier:

> What we now know for absolute certain is that it's possible to write any kind of code without gotos (including awful code).

I meant to point out the inverse, that it's possible to write good code even with gotos -- but it takes the same amount of care and diligence as writing good code in general. I don't mean to advocate the use of gotos, rather the elimination of the paranoia surrounding them.


Just in case it's not clear, the break statements in my example are to break out of the switch(), not the while(). C's switch statements are like a computed goto.


Ah, right. I forgot about break inside switch. Thanks.

You're right about the switch statements being like a computed goto, albeit a delimited, forward-only one. I still think it's silly to bend over backwards to avoid using goto in places where goto is a natural fit.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: