21 ms·
I always encourage people to go read Dijkstra's GOTO paper, instead of just its title. It's a short and easy read, almost like a blog post. If you pay attention
by ufo 4y ago
I always encourage people to go read Dijkstra's GOTO paper, instead of just its title. It's a short and easy read, almost like a blog post. If you pay attention, you can see that the he was talking about spaghetti code vs structured code. It's better when the lexical structure of the source code maps to the execution structure. That is, if you know what is the current line being executed, you have a good idea of what the global state is, which lines executed before this one, and which will be executed next.
- arp242 4y agoThe thing is that many people today have never encountered the sort of spaghetti code that Dijkstra was talking about in 1968. There's plenty of confusing and messy code around, but true spaghetti code that GOTOs all over the place and is nigh-impossible to follow has been extremely rare for a long time. I can't recall encountering it in the last 30 years. It easy to misunderstand what he was even talking about because the paper is so short, assumes you know about this context, and has no concrete examples. People quite reasonably assume it's about ugly code they've encountered, but it's actually about ugly code of a completely different kind. I'm not that old, but I was unlucky enough to have programmed in an unstructured language where GOTO was the only way to use faux-subroutines in my teens. Whatever you think as code that's difficult to follow: it's nothing compared to this.
- pjmlp 4y agoI kind of have, instead of GOTOs, we have RPC calls on the flavour of the day, that just GOTOs only make sense after doing a full diagram of call sequences. Just like 8 bit BASIC spaghetti code, only refined.
- ivalm 4y agoDo you know of a good example you could link?
- svieira 4y agoThe first part of Guy Steele's talk on Fortress: https://www.infoq.com/presentations/Thinking-Parallel-Programming/ https://www.infoq.com/presentations/Thinking-Parallel-Progra...
- arp242 4y agoSomething like this is the best I could find: https://craftofcoding.files.wordpress.com/2018/01/fortran_goto.pdf https://craftofcoding.files.wordpress.com/2018/01/fortran_go... – the last page has a full listing with arrows to show the jumps. I grew up with MSX-BASIC: https://github.com/plattysoft/Modern-MSX-BASIC-Game-Dev/blob/main/examples/src/sshooter.bas https://github.com/plattysoft/Modern-MSX-BASIC-Game-Dev/blob... – GOSUB jumps to a specific line number (RETURN returns from where it jumped). Even a fairly simple and clean example like this can be rather difficult to follow.
- klyrs 4y agoI dug around a little and found an example [1] on a reddit thread looking for examples of spaghetti code. Most of the examples on the thread were just badly written code. Irreducible spaghetti code tends to be complex state machines that cannot be rendered well in a flat format. People like to flatten those out with a trampoline pattern[2], but that can hinder performance. Malicious spaghetti involves transformations such as for (x = 0; x < 10; x++) { for (y = 0; y < 20; y++) { printf("%d %d\n", x, y); } } | | DRY v x = 0; loop_x_head: condition_val = x; condition_stop = 10; condition_var = 'x'; goto check_condition; loop_x_body: y = 0; loop_y_head: condition_val = y; condition_stop = 20; condition_var = 'y'; goto check_condition; loop_y_body: printf("%d %d\n", x, y); increment_val = y; condition_stop = 20; increment_var = 'y'; goto increment; loop_y_end: increment_val = x; condition_stop = 10; increment_var = 'x'; goto increment; loop_x_end: halt; increment: increment_val++; if (increment_var == 'x') x = increment_val; if (increment_var == 'y') y = increment_val; condition_val = increment_val; condition_var = increment_var; check_condition: if (condition_val < condition_stop) goto pass_condition: if (condition_var == 'x') goto loop_x_end; if (condition_var == 'y') goto loop_y_end; pass_condition: if (condition_var == 'x') goto loop_x_body; if (condition_var == 'y') goto loop_y_body; [1] http://wigfield.org/RND_HAR.BAS http://wigfield.org/RND_HAR.BAS [2] https://en.wikipedia.org/wiki/Trampoline_(computing) https://en.wikipedia.org/wiki/Trampoline_(computing)
- pavlov 4y agoThis. To find an example, I did a search for “Commodore PET Basic programs”. Here’s a book from 1979 that shows what spaghetti code looks like, in my opinion: http://www.1000bit.it/support/manuali/commodore/32_BASIC_Programs_for_the_PET_Computer.pdf http://www.1000bit.it/support/manuali/commodore/32_BASIC_Pro... The first program listing is on page 24 of the PDF. Try to follow the logic of the program. Why does line 400 go to 280? What paths can lead to line 400? Who knows! And this is high-quality BASIC by 1979 standards — it’s in a printed book after all. There’s an auxiliary listing after the program itself explaining the routines and variables used, but many/most programs in those days wouldn’t have this level of rigorous documentation. Deciphering the program would probably have to start by drawing a flowchart of execution paths.
- anigbrowl 4y agoWow, that takes me back. I learned to program on a PET and would have devoured this book had it been available. As it was one of our math teachers was tasked with teaching the computer classes bu didn't have any programming knowledge beyond input/output, loops, and simple calculations. Books and manuals were hard to come by.
- lamontcg 4y agoAlso learned to program on a PET in BASIC writing code that was much messier than this because I was probably 10 years old. Might be why I'm good at restructuring horrible to read spaghetti code.
- Someone 4y ago> And this is high-quality BASIC by 1979 standards — it’s in a printed book after all. I don’t think that’s true, certainly not for books of that time period. Because the whole field was changing rapidly, writers would often work under tight schedules, and customers would buy about anything because they only had magazines and books to learn from and review sites didn’t exist. I also think that’s bad Basic for the time. Certainly, comment lines before subroutines would help.
- 4y ago
- spacechild1 4y ago> but true spaghetti code that GOTOs all over the place and is nigh-impossible to follow has been extremely rare for a long time. Exactly! Recently I had the "pleasure" to work with some FORTRAN IV code from the early 60s, so I know what you mean. No functions/subroutines, only GOTOs. Even loops were done with labels. There is also a weird feature called "arithmetic IF statements" (https://en.wikipedia.org/wiki/Arithmetic_IF https://en.wikipedia.org/wiki/Arithmetic_IF). Luckily the code was pretty short (about 500 lines including comments).
- bee_rider 4y agoHmm, it is time to live Cunningham's Law I think: There’s no good way to do a do…while loop in Fortran, other than a goto.
- spacechild1 4y agoEarly Fortran already had loops, but with labels: INTEGER A(4,4), C, R ... DO 10 WHILE ( C .NE. R ) A(C,R) = A(C,R) + 1 C = C+1 10 CONTINUE Note that Fortran has evolved significantly over time. This is how you would write the same in Fortran 77: INTEGER A(4,4), C, R ... C = 4 R = 1 DO WHILE ( C .GT. R ) A(C,R) = 1 C = C - 1 END DO
- bee_rider 4y agoThose are just regular loops, though, right? I’m looking for the posttest loop, sometimes known as the do…while or until loop. There seems to be a unfortunate inconsistency in the naming of this thing.
- spacechild1 4y agoAh, right, I misread your post. DO WHILE is indeed a "normal" while loop. There doesn't seem to be an equivalent to "do { ... } while", as found in most C-style languages. > There seems to be a unfortunate inconsistency in the naming of this thing. Well, Fortran is older than C, so you cannot really blame them :-)
- jjav 4y ago> The thing is that many people today have never encountered the sort of spaghetti code that Dijkstra was talking about in 1968. Can't highlight this enough. The type of spaghetti code "goto considered harmful" was reacting to is basically impossible to create anymore, so anyone who didn't work on that type of code in the 80s or earlier probably hasn't seen it. And thus, is applying the mantra "goto considered harmful" incorrectly. (Such as trying to avoid it in C for clean error handling, when there's no reason to avoid that.) To try to replicate that experience, you'd have to write your entire application (including all libraries since libraries were often not a thing) as one single function in C. All of it, no matter how many tens of thousands of lines of code, all in one function. Then label every line. Then picture having GOTOs going every which way to any of the labels. For instance you'd preset the counter variable to some desired value and jump right into the middle of a loop elsewhere. Over time you'd surely accumulate special conditions within that loop to jump out. And so on. It's difficult to even imagine code like this today (or in the past 30 years).
- piva00 4y agoI agree, the closest modern equivalent is code that branches too much from too many places. Modern tools still help a lot to reason about it but it comes to a place where I, personally, have to create call flow diagrams on pen and paper or a whiteboard to actually understand some flows. Code with extreme branching, while dealing with state/boolean parameters that determine flow branching; error handling that can also create other branches of execution; all of that is really hard to keep in mind when reading such nightmare codebases...
- strangattractor 4y agoIf you have ever seen someone try an construct a bunch of nested IF statements with complicated conditional clauses you might think GOTO is not so bad. People have simply become better coders. There are also still GOTOs that are used in specific cases such as CONTINUE and BREAK - no labels required. If I look back it always comes back to naming and managing names of things. GOTO 100 is meaningless and one eventually runs out of meaningful names for GOTO labels. For me OOPs addressed the naming issue relatively effectively by using the data type as a namespace of sorts.
- zh3 4y agoI recall having to sort out spaghetti Fortran back in the '80s; numbers as labels (a la basic but with free-form numbering), computed gotos back and forth in the code, stuff like that. Learnt a lot from fixing that mess. Forty years later, I'll still use a C goto if the situation warrants (e.g. as a getout from deep but simple if). Maybe because having long been an assembler programmer as well, goto's are part of the landscape (if/else is effectively a conditional and unconditional branch/jump).
- deleted 4y ago[deleted]
- inlined 4y agoI used to work at Microsoft on the windows team. There it was very common to have a “goto cleanup” for all early exits. It was clean and readable. Otoh, I once was assigned to investigate an assertion error in the bowels of IE layout code. It was hundreds of stack frames deep in a recursive function that was over a thousand lines long and had multiple gotos that went forwards or backwards. That was an absolute mess and would have been impossible to debug without an recording (“time travel”) debugger.
- astrange 4y agoAny API that returns error codes has that issue; if it just returns an int you can't just set a breakpoint on the "allocate an error" method.
- mort96 4y agoTo give people some kind of an idea of what it was created in response to: Imagine writing an entire program in one single main function. The only thing you're allowed to do for flow control is goto. You can do 'goto somelabel;' for an unconditional goto, or you can do 'if (somecondition) goto somelabel;' for a conditional goto. Here's some examples of how it would look if if translated to something C-like: Loops would look like: int i = 0; loop_start: print i; i = i + 1; if (i < 10) goto loop_start; Fizzbuzz would look something like: int i = 0; loop_start: if (i % 5 != 0) goto not_fizzbuzz; print "fizzbuzz"; goto done; not_fizzbuzz: if (i % 3 != 0) goto not_fizz; print "fizz"; goto done; not_fizz: if (i % 5 != 0) goto not_buzz; print "buzz"; goto done; not_buzz: print i; done: i += 1; if (i < 100) goto loop_start; Often, this wasn't just constrained to a single function; the whole program would be constructed like this, with gotos which jump back and forth across pages and pages of code. Languages wouldn't even have a call stack with subroutines (which is why "procedural" languages -- languages with procedures -- were important enough to be given a special name). At least that's my understanding of it. I haven't lived through this, and my only experience with this kind of stuff is writing assembly, where we always make use of a call stack, so even that is in practice a procedural language. If I have gotten anything wrong, please correct me.
- fsckboy 4y agotangentially, can somebody point me to what are considered the best fizzbuzz solutions? I'm both an experienced and cs-educated coder, and I know what I would consider to be a good solution, but I have no idea what the rest of "you" are looking for. (my favored solution would be a small number of state machines running in parallel to sieve-of-eratosthenes the correct answers thus avoid innumerable divisions, but maybe that's just me and I'm old fashioned?)
- anonymoushn 4y agoSurely your compiler won't actually emit an integer modulus if you tell it to mod by a constant?
- _kst_ 4y ago
- girvo 4y agoThe last time I encountered that kind of code was when I wrote it myself in VB6 as a kid. I’ve never seen it in my decade and a half work life.
- Jtsummers 4y agoYou're lucky. If you want to feel comfortable on a plane, don't work on avionics systems written in the 1970s-1980s (and probably a lot from the 1990s). Some horrifically bad code running some planes.
- DonHopkins 4y agoBASIC. The only thing worse than gotos is line numbers.
- jamesfinlayson 4y agoYeah I saw a codebase written in Fortran 77 (the program was written in 1988 or 1989 I believe) and geez... it is almost impossible to figure out what is going on. Programming has changed a lot.
- dilyevsky 4y ago> The thing is that many people today have never encountered the sort of spaghetti code that Dijkstra was talking about in 1968. I would say js/python callback-based frameworks of today like Twisted (also c++/rust futures) is exactly that
- kevin_thibedeau 4y agoI've seen full spaghetti on recently written C driver code for an IC. The device contained a 24-bit processor that wouldn't have a compiler and its one-man dev team was necessarily doing all of the firmware in assembly. He basically wrote all of the C with assembly style control flow, goto-ing all over the place and zero high level statements.
- Natsu 4y ago> I'm not that old, but I was unlucky enough to have programmed in an unstructured language where GOTO was the only way to use faux-subroutines in my teens. Was that a Casio calculator? Because it was like that for me, only GOTOs existed. Learning about C and seeing these things called loops was a revelation because I had reinvented them with GOTOs already in my programming.
- PaulHoule 4y agoBASIC? GOSUB was so much worse than GOTO in that it had a stack for the current line but no stack for variables so you could not write recursive functions. I think Fibonacci as a recursive function is malpractice but boy was it a hassle to write Quicksort in BASIC although I had no trouble coding up an FFT (1950s FORTRAN style) from first principles in BASIC on TRS-80 Model 100 on a bus ride across Vermont. Funny though I did come to a conclusion that for the Arduino programs I wrote I didn’t need a stack at all.
- wglb 4y agoI read the paper when it first came out. At the time I was programming in Fortran which had the three branch if-statements. As you pointed out, that was a special kind of hell. The paper rang very true. However we did all take it to the extreme and go for zero goto with quite a fervor.
- deleted 4y ago[deleted]
- josh11b 4y ago[1] on structured concurrency gives good context for Dijkstra's GOTO paper and how it is applicable today. [1] https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/ https://vorpus.org/blog/notes-on-structured-concurrency-or-g...
- tialaramex 4y agoSure, it's a good letter, and indeed it's about this problem where people use a go-to or jump control flow with no structure whereas by the time he wrote that letter there were well understood structures (like: functions) to prefer. However, while I sympathise with a C programmer who feels goto is necessary in their language, I think that speaks to a problem in the language rather than the programmer. If you have better structural support in your language you can express the things the author (of the link, not Dijkstra) wants to express without needing this unstructured go-to. For example Rust's break 'label value; allows us to mark any compound expression with the 'label, and then say from anywhere inside that expression but nowhere else that we've decided the value of the expression overall and here's what it is. This doesn't feel that different from what is being done here with goto, except for two crucial things as a result of being structured: 1. Rust will type check this, if this region of the program picks a Dog, our break needs to provide a Dog, it can't just shrug and expect the program to continue without one. This means maintenance programmers don't need non-local reasoning, this region of the program does, in fact, always pick a Dog, albeit the break 'label value is something to look closely at if you're reading that region itself. 2. We cannot do this, even by mistake (e.g. as a result of copy-paste) across scopes. If you try to break 'label result from the cat care loop into the dog loop earlier in the same function, that just doesn't compile, whereas the C goto has no problem attempting that (a good C compiler should notice if you try to do something really egregious, but good luck).
- deleted 4y ago[deleted]
- zozbot234 4y ago> It's better when the lexical structure of the source code maps to the execution structure. Sure, but that has nothing to do with whether the GOTO statement should be used. The semantics of GOTO are arguably quite clear; they amount to setting a different continuation for the running program. (Semantically, this implies that the precondition of the GOTO statement is made a possible precondition for the label that the statement jumps to; and execution simply does not proceed to the next statement.) There are even "relooper" algorithms to reconstruct a structured program from patterns in the idiomatic use of GOTO statements: they are used in compiling to structured object languages such as WASM code. Using GOTOs in an idiomatically sensible way (avoiding spaghetti code) may be less readable than writing actual structured blocks, but only slightly so.
- kbumsik 4y agoAnd the original paper is published even before the first version of C was introduced.
- sedatk 4y agoThat wasn’t even Dijkstra’s title. It was Niklaus Wirth who edited the title in. I mention that in Street Coder.
- bazoom42 4y agoInterestingly, many seem to want to project their own opinion upon Dijkstra. Dijkstra is clearly arguing for a “single entry single exit” style. But modern consensus seem to uphold single entry but accept multiple exits from a block. Break, continue, early returns, exceptions - all are example of multiple exit. These are more constrained than gotos but nevertheless Dijkstras argument applies to them also. I personally belive early returns can greatly improve readability (when not nested too deep) an that exceptions are typically cleaner than the alternative. But I acknowlede Dijkstra would disagree.
- mFixman 4y agoIIRC multiple entry points were common in Dijkstra's times. Break, continue, and early returns always return to the end of the block, unlike goto in the '70s. Exceptions are more complicated, and the cause of a lot of inunderstandable programs.