1. 6
  1. 10

    You’d get the same number of possible behaviors if you used a while loop instead of a goto.

    1. 1

      Haha yeah I had the same immediate reaction. “Wait, this is a human-understanding problem, not a stats problem. Loops are still okay!”

    2. 8

      “goto considered harmful” has a historical context of a goto that allowed much more than C’s goto does. You could jump into any code, not just to a label in the same function. It made it hard to reason about a function because you could not tell whether it did not some weird jumps just based on its signature.

      1. 9

        I also think it’s important to consider the context. “Goto considered harmful” was written at a point where a common way to program was to have only goto, and no other flow control. We’re not talking about programs which use while-loops and if-statements and may have the occasional goto statement like we see today. “Goto considered harmful” was a part of popularising the concept of if statements and while loops. Mindlessly citing the original “goto considered harmful” today without considering that context is, well, harmful.

        1. 8

          “longjmp(3) considered harmful” just doesn’t have the same ring to it…

          1. 1

            longjmp(3) isn’t the same as an unconstrained goto though (which is jumping to any point in the program, not just the same function, or to a location set by setjmp(3)).

          2. 7

            Yup. I cut my teeth (ages 11-16 or so) programming in BASIC. That’s O.G. BASIC, whose only control flow was GOTO, GOSUB / RETURN, and IF-THEN (a conditional GOTO.) And statement labels were line numbers. There were no functions, only subroutines, and they had no parameters or return values, only (global) variable assignments by convention.

            If you haven’t done it, you cannot imagine trying to code this way. The metaphor “spaghetti code” is a lot more literal and visceral than you imagine … more like Nyarlathotep, The Crawling Chaos. We call C “structured assembly”, but BASIC was less structured than assembly. In assembly, you have an unlimited number of variables (not 286, from A to Z9) and can use the stack to impose discipline upon them. In assembly you can insert a new line between two others without it being a major contortion. I remember having to add a pair of GOTOs just because there wasn’t room to insert new code in place.

            Apologies that this has turned into an “uphill to school both ways in a blizzard” rant. I’m off my meds today. But the example in that article shows that the youngster probably has no conception of the Lovecraftian depths of cosmic horror that prevailed before Structured Programming. Looking back, it’s a miracle that the Gen X coders like me who learned their craft on Apple ][s, PETs and TRS-80s weren’t permanently scarred. I remember UCSD Pascal in college being like the Promised Land.

            (PS: I’m aware Dijkstra’s GOTO paper was not referring to a slum language like BASIC… the FORTRAN of the day was slightly saner, but not much.)

            1. 3

              It would also completely smash any concept of a “stack frame” or “coherent assignment of spilled variables to stack locations”.

            2. 5

              This is one case where the comments on Lobster are more informative than the article in question.

              1. 4

                In my experience that’s very often the case. Especially since authors, people of that field ( especially @david_chisnall ) can give a wider background of information.

              2. 4

                Yes, C family’s goto makes control flow much harder to reason about, but I have never heard the argument “goto is a tool, and tools should be used” in favor of using it.

                The only times I see goto in production code are where the alternative is worse. The common cases are:

                • Interpreters - I feel this doesn’t fall under the usual “goto” arguments, but if I don’t mention it I’m sure someone would say I missed it :D Anyway, computed goto is an absolute requirement for high performance interpreters

                • continue/break to an outer loop. Modern languages have labeled loops and with which you get much saner control restrictions, the alternative is adding a lot of state to track whether you should be continuing execution as you break out of nested loops.

                • Cleanup. The heavy nesting, conditional logic, and/or duplicate cleanup that C code accumulates necessitates “goto cleanup/fail”. This shouldn’t be a problem in new code, except people continue to cargo cult the anti-C++ wagon. I get it, C++ has some bad features, but no one requires you to use them, and most major c++ projects explicitly ban them.

                1. 10

                  Yes, C family’s goto makes control flow much harder to reason about

                  It’s worth noting that C’s goto is not the kind of goto that Dijkstra was complaining about. In C, goto means that any function is a set of basic blocks with arcs between them. Generally, functions have on the order of tens of basic blocks and the number of arcs is fairly small. LLVM IR is exactly this representation and it’s perfectly amenable to analysis. The only real complexity in C/C++ comes from the fact that you would be able to jump out of scopes and skip over initialisation (or even over stack allocation of a variable) if you allow unconstrained local goto. C and C++ have some limitations on exactly what goto is allowed to do precisely to avoid the worst of these cases.

                  In contrast, the common GOTO pattern from the essay’s era was a non-local GOTO. This could jump anywhere in your program. Functions (subroutines) didn’t exist as encapsulation boundaries: you could jump from the middle of one function to the middle of another. Lexical scoping wasn’t a thing that you could use for lifetime management, because control flow wasn’t bound to lexical scopes.

                  The problem from the picture in the article was far worse. You can reason about the set of control-flow arcs with local goto quite easily because you can explicitly see all labels (not every basic block starts with a label that you can use for goto in C) and you can see the named arcs. In contrast, with non-local goto, you could jump anywhere in a program. Often, languages of the era used line numbers and compiled statement-at-a-time, so you could jump to any statement, anywhere in the program. Control would then continue from there, if you wanted to return then you’d need to build your own mechanism for communicating the next jump address into the program.

                  Amusingly, I’ve read another article by Dijkstra where he described a call instruction (jump and push return address onto the stack) as unnecessary premature optimisation.

                  1. 2

                    you could jump from the middle of one function to the middle of another

                    It’s basically JMP exposed to the programmer, like a very leaky abstraction

                    1. 1

                      I mean reason about the goto as a human, not as a machine. The structurally constrained goto that are return, break, and continue make much ore sense to someone reading the code.

                      1. 3

                        The same applies. In a function with half a dozen labels and five goto statements, you can pair each goto with the corresponding label and visualise the CFG relatively easily, even in the worst abuse of goto that I’ve seen in C, you have on the order of a hundred control-flow arcs. With a program in a language that doesn’t support structured programming, every statement is a possible jump target and every goto can go anywhere in the program. Trying to keep this in your head is basically impossible.

                  2. 2

                    This post is a good illustration of the combinatorial explosion caused by an unfettered goto, but is undermined by the strawman setting it up.

                    Whenever the subject of goto comes up there is always a slew of people of people who say that goto being harmful is considered an overreaction.

                    Saying “goto is a tool, and tools should be used” is somewhat of a simplistic argument.

                    I have seen two main arguments in “defense” of goto :

                    • goto can be useful in specific circumstances;
                    • goto in modern languages is much more restricted than the 1968 goto. In particular, it generally cannot jump arbitrarily.

                    These arguments are even discussed in this other post of the author.

                    Edit: I was unfairly harsh in the first version of this comment, sorry.

                    1. 2

                      The real reason is that it doesn’t well support the decomposition of a complex problem into individual parts that can be independently written, maintained, reused, and then recombined into an application. The callable *procedure” or “subroutine” does.