Live data from Hacker News

Goto (2007)

beej.us

201–207 of 207 posts

Re: Goto (2007)

#201

Probably coming too late to the discussion, but commenting anyway... The issue I think is that people today read "GOTO Considered Harmful" without really understanding the world at the time. Other than simple integer FOR loops, basically all control-flow in the FORTRAN of those days was accomplished with GOTO -- and to numbered lines, not labels. Things we take for granted in all languages today like { code blocks }…

> " The issue I think is that people today read "GOTO Considered Harmful" without really understanding the world at the time. ... Just because it was a bad idea to do everything with a GOTO 60 years ago shouldn't mean don't do some things with goto today. " Every time this comes up, I always encourage people to read an excellent analysis by David Tribble " Go To Statement Considered Harmful: A Retrospective " that go…

This is a good balanced article.

What is often missing from these goto discussions is the dialog between Knuth and Dijkstra on this topic.

There is another discussion between the two of them about a problem requiring no less than four stacks to understand.

Re: Goto (2007)

#202

Earlier quoted context omitted.

In plenty of environments, there's a very limited number of calls you can make before the program fails entirely. For instance, in GW-BASIC, your namesake, the limit is less than 100 calls. There are plenty of real useful production langauges where your stack will run out before 1000 frames of depth. But on the other hand, it's quite reasonable to write a state machine which is expected to make [mb]illions of state t…

Oh you're bringing back memories of childhood. When I wrote in GW-Basic, I only used gotos. Most of the example code that I had access to used gotos. I was 11 years old at the time. > There are plenty of real useful production langauges where your stack will run out before 1000 frames of depth That wouldn't surprise me in an embedded environment. But if that's the case for a run of the mill general purpose programmin…

> But if that's the case for a run of the mill general purpose programming language that's running a typical web application, I'd be shocked.

Python's default recursion limit is 1000.

Re: Goto (2007)

#203

Probably coming too late to the discussion, but commenting anyway... The issue I think is that people today read "GOTO Considered Harmful" without really understanding the world at the time. Other than simple integer FOR loops, basically all control-flow in the FORTRAN of those days was accomplished with GOTO -- and to numbered lines, not labels. Things we take for granted in all languages today like { code blocks }…

I thought the problem with unstructured goto is that it is extremely hard to reason formally about code that uses it.

Re: Goto (2007)

#204

Probably coming too late to the discussion, but commenting anyway... The issue I think is that people today read "GOTO Considered Harmful" without really understanding the world at the time. Other than simple integer FOR loops, basically all control-flow in the FORTRAN of those days was accomplished with GOTO -- and to numbered lines, not labels. Things we take for granted in all languages today like { code blocks }…

Can you give an example of where you've used goto in C or a C-like language? Or a rough estimate of the number of times you've found goto to be the best solution? I agree with your post in principle, but in practice I can't think of a single example where I've used a goto that wasn't eventually refactored to something better that didn't have the goto. Edit: Probably the most common example (and one given early in the…

For a rough estimation, you can use my bc at [1] which uses goto extensively for safe and effective use of setjmp() and longjmp(), while having no memory leaks (to my knowledge).

When going through the code, though, be aware that some goto's are in #define's to make it easier to work with setjmp().

If you're lazy, it's about 10k loc with about 100-200 uses of goto.

[1]: https://git.yzena.com/gavin/bc

Re: Goto (2007)

#205

Goto is a useful precursor for writing a tail recursive solution. For instance, say we have a state machine for recognizing that the last four button presses were 1234. int keypad() // returns 1 if correct key is entered, otherwise loops forever { keypad: switch (getkey()) { case 1: goto got_1; default: goto keypad; } got_1: switch (getkey()) { case 2: goto got_2; case 1: goto got_1; default: goto keypad; } got_2: sw…

I think the better way to create a state machine would use another switch instead of the top-level goto like: int keypad() { int state = 0; // 0 = start, 1 = seen '1', 2 = seen '12', 3 = seen '123' while (true) { int key = getkey(); switch (state) { case 0: switch (key) { case 1: state = 1; default: state = 0; } break; } case 1: switch (key) { case 1: state = 1; case 2: state = 2; default: state = 0; } break; } case…

I don't see how that is better. Every one of your "state = N" assignment is just a goto in disguise, which requires the machinery to go back to the top and then jump somewhere again.

Someone looking at "state = 1" has to understand that state is a variable which controls the outer switch, and that 1 is refers to "case 1:" of that outer switch.

You have a proliferation of "case 1:" repetitions, so the reader has to follow the indentation carefully.

The goto is not a simple goto, either. When we assign "state = 1" it doesn't just mean to branch to the "case 1" node, but to go through the "key = getkey()" initialization.

The whole thing is less efficient than goto.

Specifically to C, you have more lines of code now. You remembered your break statements in the outer switch, but in the inner switches, you carelessly changed the gotos to assignments without adding the required break statements, so you have a bug in every inner switch. When you fix those, you will have some seven more lines of code.

Note how my original code has no variables at all, and neither does the transliteration into tail calls. You have two variables: key and state, plus you still have the same underlying goto graph structure.

The only saving grace of this arrangement is that it factors out the common action of calling getkey() into one place which is only important if we can't turn that common action into a tiny function call with just two or three arguments.

The property in my graph that getkey() is called in multiple places is a key property; it helps to farm off each block directly into a corresponding tail calling function in the mechanical transformation to the tail call graph.

I used to write all state machines this way, with labeled states, and a loop around a switch and input processing in one place at the top.

Quite recently, I did some ad hoc comaparative studies which convinced me I won't be doing that any more; I will be using disciplined goto graphs.

Re: Goto (2007)

#206

Earlier quoted context omitted.

I think the better way to create a state machine would use another switch instead of the top-level goto like: int keypad() { int state = 0; // 0 = start, 1 = seen '1', 2 = seen '12', 3 = seen '123' while (true) { int key = getkey(); switch (state) { case 0: switch (key) { case 1: state = 1; default: state = 0; } break; } case 1: switch (key) { case 1: state = 1; case 2: state = 2; default: state = 0; } break; } case…

I don't see how that is better. Every one of your "state = N" assignment is just a goto in disguise, which requires the machinery to go back to the top and then jump somewhere again. Someone looking at "state = 1" has to understand that state is a variable which controls the outer switch, and that 1 is refers to "case 1:" of that outer switch. You have a proliferation of "case 1:" repetitions, so the reader has to fo…

Also note that switch (state) { case STATE_LABEL: ... } is a form of computed goto. Every time we use switch in a C program, we are using computed goto.

The switch statement escapes the disparagement that is lobbed at goto for two reasons.

1. It doesn't have "goto" anywhere in its name. (Neither does an assembly language JMP instruction, yet it is still goto).

2. It has a limited scope: whereas goto can jump to any label anywhere in a function, a switch only branches within a statement. (But anywhere within that statement; into any level of nesting inside any sub-statement!)

If you wrap the bulk of your function with a giant switch inside a dispatch loop on a state variable, then, effectively, the (2) limited scope defense of switch no longer applies: you have perpetrated a bare-faced computed goto scoped over the bulk of the function body.

Re: Goto (2007)

#207

Probably coming too late to the discussion, but commenting anyway... The issue I think is that people today read "GOTO Considered Harmful" without really understanding the world at the time. Other than simple integer FOR loops, basically all control-flow in the FORTRAN of those days was accomplished with GOTO -- and to numbered lines, not labels. Things we take for granted in all languages today like { code blocks }…

> " The issue I think is that people today read "GOTO Considered Harmful" without really understanding the world at the time. ... Just because it was a bad idea to do everything with a GOTO 60 years ago shouldn't mean don't do some things with goto today. " Every time this comes up, I always encourage people to read an excellent analysis by David Tribble " Go To Statement Considered Harmful: A Retrospective " that go…

> "Dijkstra later abandoned the search for program provability and turned instead to the study of techniques for correct program derivation"

It's odd how even today very few people in the "formal verification" world understand this distinction. Naturally verifying a derived program is trivial, since it's literally a proof by construction. I've attempted many times to explain this distinction, and they inevitably just start babbling about the limitations of the tooling they use. A sad case of man with a hammer syndrome and probably an illustration of my own failure to achieve clarity I guess.

> "However, to some extent Dijkstra's principle has not been fully realized when we observe the complexity that must be dealt with by real-world programming tasks, such as multitasking, multithreading, interrupt handling"

Dijkstra designed the first proven correct interrupt handler for a multitasking OS two years before the Goto Letter[1][2] so I think we can rest assured that he was aware of these issues.

[1] https://en.wikipedia.org/wiki/THE_multiprogramming_system

[2] https://www.cs.utexas.edu/users/EWD/transcriptions/EWD01xx/E...

Post reply on HN