[HN Gopher] Goto
       ___________________________________________________________________
        
       Goto
        
       Author : bruce343434
       Score  : 217 points
       Date   : 2021-10-21 11:58 UTC (11 hours ago)
        
 (HTM) web link (beej.us)
 (TXT) w3m dump (beej.us)
        
       | bodyfour wrote:
       | 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 } didn't exist.
       | 
       | Indeed the first thing you do when you try to understand code of
       | that era is to print it out, get your markers out, and start
       | drawing arrows all over the green-bar paper so you can start to
       | make some sense of where control flow is going.
       | 
       | In that world, yes, GOTO was a big problem and its use was
       | rightly replaced with the structured control flow that we all use
       | today. But that world has also been nearly gone for 30 years now.
       | It's completely unrelated to using a C "goto" statement with a
       | well-chosen label name in order to accomplish some otherwise-
       | awkward control flow.
       | 
       | On projects I've worked on I tend to develop a reputation for
       | heavy goto use and I do so unapologetically. Sometimes it
       | honestly is the cleanest way to implement something and I don't
       | think it should be feared one bit.
       | 
       | 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.
        
         | moffkalast wrote:
         | Yeah to those still adamant about it, if you've ever used a:
         | 
         | - function call
         | 
         | - if sentence
         | 
         | - any kind of loop
         | 
         | You've used a GOTO under the hood. I hope you can live with
         | yourself, you monster :)
        
           | masklinn wrote:
           | > You've used a GOTO under the hood.
           | 
           | That it's under the hood is the point of using constrained
           | control-flow control. It's significantly easier to reason
           | about `if` and `while` and `for` than about `goto`. That is
           | basically the same reason functional programming have more
           | HoFs than `fold`. You can do everything with `fold` _and that
           | 's a problem_ because it creates way more cognitive overhead
           | for the reader, they can't know what the code is trying to
           | achieve until they get all the details, nor is the compiler
           | able to check anything.
           | 
           | Likewise goto.
        
           | agent327 wrote:
           | You put a smiley so I suppose it's possible you know better,
           | but this kind of shallow, thoughtless critique is just
           | painful to read, especially when following such a well-
           | presented, historically accurate description of the debate
           | surrounding goto. This is especially true given that Dijkstra
           | himself refutes your argument, such as it is, in the opening
           | paragraph of his seminal paper.
        
           | brundolf wrote:
           | But there's a reason we come up with sanctioned, constrained
           | concepts built atop more powerful lower-level ones.
           | Constraints in code benefit maintainability, correctness, and
           | sometimes even performance.
           | 
           | "Yeah to those still adamant about it, if you've ever used
           | statically-typed code, you've used untyped assembly under the
           | hood. I hope you can live with yourself, you monster :)"
        
           | 1-more wrote:
           | a case statement is the one where the GOTO peeks out the
           | most, imo.
        
           | andi999 wrote:
           | The real hidden gotos are:
           | 
           | - state machines - early return of a function
        
           | GrumpyYoungMan wrote:
           | What's even worse is that, at the machine language level,
           | condition and unconditional jumps are all that there are;
           | we've all been using GOTOs constantly without even knowing
           | it. :)
        
         | GrumpyYoungMan wrote:
         | > " _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 goes over
         | Dijkstra's essay line by line and explains what it means in a
         | more modern context: http://david.tribble.com/text/goto.html It
         | really should be required reading in CS programs.
         | 
         | I've used and still use goto in the few niches it makes sense
         | to, primarily error handling and multi-level break in C/C++.
        
         | westcort wrote:
         | What I want is a language that consists ONLY of GOTO
         | statements. The Turing tape concept indicates that it should be
         | possible.
        
           | schoen wrote:
           | Subtract and branch if negative is more or less that:
           | 
           | https://en.wikipedia.org/wiki/One-
           | instruction_set_computer#S...
        
         | 0xffff2 wrote:
         | 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 article) is deeply nested loops. This is a good example of
         | where I'm very unlikely to use goto. Almost certainly, I will
         | prefer to hide one or more of the inner loops inside of a
         | function call.
        
           | throwaway09223 wrote:
           | There's a good real world example of goto for progressive
           | cleanup here: https://github.com/square/pam_krb5_ccache/blob/
           | master/pam_kr...
        
           | [deleted]
        
           | codr7 wrote:
           | https://github.com/codr7/ampl/blob/main/src/ampl/eval.cpp
        
             | slaymaker1907 wrote:
             | Replying to infiniterand, a table of function pointers is
             | going to generally have way more overhead than an indirect
             | goto. An indirect goto is essentially a single assembly
             | jump instruction. This is also often faster than a while
             | loop plus switch since it is friendlier to the branch
             | predictor.
        
             | InfiniteRand wrote:
             | I'm sure there's probably a reason for doing things this
             | way, but it seems like that code would make more sense as a
             | function pointer table and a series of functions. Was that
             | not preferred for aesthetic reasons or for some specific
             | technical reason?
        
               | codr7 wrote:
               | Speed.
               | 
               | Your suggestion adds indirect memory access plus function
               | call, for every instruction.
               | 
               | I've tried every other way I can think of, and nothing
               | runs faster from my experience.
        
           | bodyfour wrote:
           | > deeply nested loops. [...] I will prefer to hide one or
           | more of the inner loops inside of a function call.
           | 
           | And I would too... but sometimes there are too many local
           | variables involved to do that cleanly. And, ultimately, if
           | you are making your code less clean to avoid using a goto
           | then you avoided unwisely.
           | 
           | > Can you give an example of where you've used goto in C or a
           | C-like language?
           | 
           | Well there have been several fairly normal examples cited by
           | others. Let me set out my stall and present a more
           | controversial one.
           | 
           | Suppose I have a switch() block (which is sorta a glorified
           | goto already!) and I have two cases that have the same
           | epilogue. For example:                 switch
           | (get_next_thing_to_do()) {       case ACTION_A:
           | do_action_a();           goto common_post_action_cleanup;
           | case ACTION_B:           do_action_b();
           | common_post_action_cleanup:           cleanup_something();
           | z = nullptr;           x = x->next;           // yadda yadda
           | break;             case ACTION_C:           do_action_c();
           | // NOTE: we don't need to do the "cleanup" here
           | break;        }
           | 
           | Now the first thing you should try is to put all of the
           | shared code in its own function. Again, this isn't always
           | practical if what you need to do effects a lot of local
           | variables. It also means that those "cleanup" steps are now
           | implemented far away from the rest of the logic which might
           | hurt comprehensibility on its own.
           | 
           | Another option is to just suck it up, repeat the code twice,
           | and then just trust that the compiler will clean it up.
           | First, that won't be as attractive if you have to do it 10
           | times instead of twice. Second, you're now violating the DRY
           | principal which to me is sacrosanct. Experience has taught me
           | that if someone updates the cleanup code for ACTION_A they
           | _will_ forget to update ACTION_B at the same time. If you
           | care that the two cases always end with the same epilogue you
           | need to have a single copy of the code.
           | 
           | You can try to get back into compliance with DRY by using
           | something like a C macro, but now you're not winning the war
           | against ugly code.
           | 
           | Finally you could just put the cleanup code after the
           | switch(), either by retesting the enum value (requiring it to
           | be stored in a temporary variable) or by adding some other
           | new cleanup_needed boolean flag. But now we're _adding_
           | control flow that wasn 't there before. I personally believe
           | that doesn't help readability or maintainability. You're now
           | just adding more variables to understand and probably getting
           | worse generated assembly to boot.
           | 
           | While many programmers will look at this and feel vaguely
           | icky about the presence of a "goto" if you really stop and
           | _look_ at the block of code it 's actually quite
           | readable...at least as long as the label and the goto are
           | near each other. It is immediately apparent that two "states"
           | are sharing some code and why.
           | 
           | This little example is obviously made up. However I've spent
           | a lot of my career writing things like hand-crafted state
           | machines and situations exactly like this (where two "states"
           | are different but share the same epilogue) comes up _all_ the
           | time. State machine code like this is famously hard to read
           | but often sharing code via goto like this is the least-bad
           | option for expressing non-hierarchical control flow.
        
           | anfelor wrote:
           | Another example not covered on the article is tail call
           | optimisation. GCC is not very good at it and manually adding
           | it using goto can sometimes speed code up by ~20%. Very
           | useful when writing down a complex recursive algorithm that
           | you can't easily express as a loop.
        
           | davidwf wrote:
           | It's a very common pattern for C memory allocation checking.
           | A public example I know off the top of my head can be seen
           | here: https://github.com/zedshaw/learn-c-the-hard-way-
           | lectures/blo... (the implementation of the CHECK macro).
           | That's in a C tutorial but I've implemented a version of that
           | macro frequently.
           | 
           | Let's say you need to dynamically allocate two buffers in a
           | function and want to make sure they are freed at the end of
           | your call. You can use this macro like so:
           | int two_bufs(int n, int m) {         int *buf_1 = NULL;
           | int *buf_2 = NULL;         buf_1 = calloc(n, sizeof(int));
           | CHECK(buf_1);         buf_2 = calloc(m, sizeof(int));
           | CHECK(buf_2);               // ... lots of cool things with
           | buf_1 and buf_2            error:         free(buf_1);
           | free(buf_2); // Safe if null            }
        
             | jefftk wrote:
             | You have "free(buf_2); // Safe if null", but if
             | CHECK(buf_1) turns into a "goto error", won't buf_2 be
             | uninitialized? And so can take on any value?
        
               | davidwf wrote:
               | You are correct, will edit. C is hard, writing C in the
               | browser sans coffee is harder. :-)
        
               | jefftk wrote:
               | I do think this illustrates one of the issues with goto:
               | normally the compiler would be able to warn that you were
               | using buf2 potentially uninitialized, but I think you
               | wouldn't get a warning in this case.
        
               | Jtsummers wrote:
               | One of the examples in the linked article showed the
               | compiler emitting a warning when a variable wasn't
               | initialized because the goto skipped past that line, it's
               | in 31.7. I don't know what compilers will or will not
               | give you that warning, but at least the one used for the
               | article does. So it ought to catch the problem with the
               | initial version of the example above as well.
        
               | lilyball wrote:
               | The Clang Static Analyzer could probably find this, if
               | the compiler itself doesn't notice.
        
           | NtGuy25 wrote:
           | If you have a dispatcher or an infinite state machine, say an
           | emulator. Or even just anything in general with cleanup, you
           | use goto to "goto ERRORCLEANUP". So you have one exit
           | condition to clean up. This is recommended in CERT c.
        
           | adrian_b wrote:
           | Few programmers use goto, because they have been
           | indoctrinated against it and they have no understanding about
           | when goto is bad and when goto is good, but there are many
           | cases where using goto is better (i.e. more clear and/or
           | faster) than what most programmers write now in such
           | occasions.
           | 
           | In some of those cases there are alternatives that could be
           | better than goto, but they cannot be used because they need
           | features that are missing from the programming languages
           | popular now.
           | 
           | One example is state machines. Many programmers when having
           | to implement a state machine use a state variable, e.g. an
           | enumeration and a big "switch" or "case" structure, depending
           | on the language, with a branch for each state.
           | 
           | This kind of implementation follows the method taught now to
           | avoid goto's by adding unnecessary variables that are used to
           | store some state when some condition is detected with the
           | purpose to make a jump later with an extra if or switch
           | instead of doing a jump with a goto immediately when the
           | condition has been detected.
           | 
           | This method of eliminating goto's is pretty stupid, because
           | it not only adds useless variables but it also doubles the
           | number of conditional branches, because each
           | 
           | if (condition) goto label;
           | 
           | is replaced by:
           | 
           | if (condition) variable = value;
           | 
           | ...
           | 
           | if (variable == value) {...} or switch (variable) {...}
           | 
           | Besides the accesses to non-cached memory, the conditional
           | branches are the main causes of low performance, so doubling
           | the number of conditional branches can result in much worse
           | performance.
           | 
           | When the second conditional branch is a "switch", that is
           | even worse, because it is typically implemented as an indexed
           | jump, which will be frequently mispredicted.
           | 
           | Instead of using a state variable and a "switch" with
           | branches for states, the right implementation is to have
           | sequences of statements for the states, with the first
           | statement in each sequence labeled with the name of the
           | state.
           | 
           | During each state, when the condition for the transition to
           | another state is detected, a goto is executed, with the name
           | of the next state.
           | 
           | Anyone who believes that this is less clear or more difficult
           | to read than the variant with state variables is delusional,
           | because in the variant with goto's you see immediately the
           | effect of a transition condition, while in the variant with
           | state variables you just see some value being stored and you
           | do not know its purpose until possibly much later when you
           | discover that it is tested for a second conditional branch.
           | Even after you see this use, you are not certain that this is
           | the only place where the variable is used, it might also have
           | other uses and you might need extra time until you are
           | certain about what the program really does.
           | 
           | A more elegant solution for state machines is possible only
           | in languages where tail call optimization is guaranteed.
           | 
           | In such languages each state may be implemented as a separate
           | function and the goto's may be replaced with function
           | invocations in final positions, which are transformed by the
           | compiler into jumps.
           | 
           | While not so many programmers need to write state machines,
           | there are much more frequent uses where goto is superior,
           | e.g. for error management in cases where exceptions are
           | inappropriate, but describing when, why and how would be
           | long.
           | 
           | In general all the problems for which goto was considered
           | harmful are not solved by eliminating goto but by restricting
           | its behavior.
           | 
           | There was a series of research articles by various authors,
           | published between 1970 and 1975, whose conclusion was that
           | the right kind of goto should be allowed to make only forward
           | jumps and that the labels must have a scope restricted to the
           | block in which they are declared. Therefore a goto should be
           | able only to exit from a block but not to enter a block.
           | 
           | An example of a programming language that had this restricted
           | form of goto was the language Mesa, from Xerox.
           | 
           | In C however, it would not be possible to restrict goto only
           | to forward jumps without simultaneously adding guaranteed
           | tail call optimization.
        
           | InfiniteRand wrote:
           | It's good for a simplified version of exception handling for
           | C (granted C does have setjmp/longjmp but that's more like
           | the complicated version of exception handling)
           | 
           | If you hit an error condition in deeply nested logic and need
           | to go to common error handling code or go to a recovery
           | point, then goto makes sense.
           | 
           | Often times in these scenarios simplifying the logic is also
           | an option, but that isn't always the case, especially when
           | dealing with external interfaces that have a lot of potential
           | error conditions that cannot be dealt with easily from the
           | immediate caller (I'm thinking primarily of system
           | interfaces, but also potentially library interfaces).
           | 
           | From what I've seen this is the only really good scenario for
           | goto and I think the examples in the article more or less
           | fall into this category of escaping from deeply nested error
           | cases.
        
             | brundolf wrote:
             | Makes me realize that the way some people use caught-
             | exceptions is actually just reaching for goto in a language
             | that doesn't have it
        
           | jjtheblunt wrote:
           | state machines a la finite automata, which (for examples) are
           | generated by compiler parser generators like yacc ?
        
           | jjav wrote:
           | Think of everythin which in java you would put in the catch
           | and finally blocks. Those are nearly always most cleanly
           | handled in C code by doing a goto to the cleanup/return
           | section at the end of the function.
           | 
           | You could work around this via tons of deeply nested blocks,
           | but that makes the code very unreadable. The goto cleanup
           | pattern makes the code intuitive and very readable.
           | 
           | I still have a printout somewhere of an application from the
           | 80s, maybe a dozen pages in small font, every few lines it
           | jumps via goto to somewhere else, depending on state (global
           | variables, of course). It was a serious nightmare to figure
           | out where the code is going. That was the historical context
           | of "goto considered harmful". It sure was! Code like that
           | doesn't exist today, the lessons have been learned. It had
           | nothing to do with clean jump to a cleanup section (which is
           | what a finally{} block is, after all).
        
           | mumblemumble wrote:
           | What's wrong with the examples in the article?
        
         | blntechie wrote:
         | My first job was writing VB.NET code and it was hammered to us
         | not to use GOTO. Once for a critical production bug, using GOTO
         | was the quickest solution to fix and I used it with a comment
         | to not fire me. And that code lived on for years.
        
           | ehutch79 wrote:
           | Sometimes a little bit of poison can make a great medicine.
           | 
           | Goes both ways though.
        
         | CJefferson wrote:
         | I agree with you completely.
         | 
         | I once made a poster (I wish I'd kept it) where I was tracing
         | out the behaviour of a single mega function with around 50
         | labels in it, and gotos all over the place.
         | 
         | I worked on a reimplementation, slowly picking apart the
         | function into subfunctions, while loops, recursive function
         | calls, etc. Took me about 2 weeks.
        
           | munificent wrote:
           | That must have been so satisfying once it was done.
        
           | prox wrote:
           | You maybe the right person to ask... Aren't function /
           | functioncallers a form of goto?
        
             | CJefferson wrote:
             | Technically yes, all code eventually compiles to goto, or
             | goto equivalents (simplifying slightly)
             | 
             | However, the idea behind "goto considered harmful" is
             | almost all code can be written with function, loops and
             | if/else statements and get just as efficient, and much,
             | much easier to understand.
        
       | continuational wrote:
       | That error handling is extremely error prone. It may be the only
       | option you have in C, but that doesn't make it any better.
        
       | drodil wrote:
       | The most amazing goto is:
       | 
       | void main() { goto http; printf("Hello world!");
       | http://www.hello-world.com }
        
         | sltkr wrote:
         | This won't actually compile, even if you format it correctly,
         | because a label must be followed by a statement.
        
       | panax wrote:
       | When you are forced to follow stupid rules like having only one
       | exit point in a function then goto can be used a lot in place of
       | that, but what are we supposed to do if you are not allowed to
       | both use goto or have multiple returns in a function (in C) ?
        
         | zarzavat wrote:
         | > what are we supposed to do if you are not allowed to both use
         | goto or have multiple returns in a function (in C)
         | 
         | Move jobs.
         | 
         | But seriously this is a good point. goto is practically
         | necessary to manage memory in C. Having only one return point
         | is justified in functions that manage heap memory. Standard C
         | has no RAII, defer, finally, or any other alternative but goto.
        
       | me_me_me wrote:
       | The only acceptable use-case for goto I came across was for error
       | handling and cleanup.
       | 
       | If there are multiple points of code failure that all require
       | same cleanup procedure then one section at the bottom is
       | acceptable and can actually keep the code cleaner.
       | 
       | Anything else looks like bad design that ends up producing hard
       | to follow code.
        
         | mikevm wrote:
         | Yes, this is pretty much how it is being used in C code. The
         | other acceptable use-case is breaking out of nested loops.
         | 
         | There's also `setjmp()`/`longjmp()` but that's even more rarely
         | used.
        
           | einpoklum wrote:
           | longjmp() is the C-language version of going-to labels in
           | other functions.
           | 
           | Its basic "use" is not having to unwind the stack; but
           | moreover, it lets you avoid cleanup and resource de-
           | allocation. Usually it doesn't make sense to do that (you get
           | memory leaks), but if, say, your memory allocations happen in
           | some kind of arena - you can just leave it in any junk state
           | and then proceed to clear it altogether after your setjmp()
           | instruction.
        
             | SAI_Peregrinus wrote:
             | The most common use of setjmp/longjmp I've seen (in
             | embedded development) is to have coroutines in C. The
             | original use AFAIK was usually to implement exceptions of
             | some variety. Neither use is that common these days.
        
         | zelos wrote:
         | I remember working with two C teams where one did:
         | rc = do_something();       if (!rc) {         rc =
         | do_something_else();       }       ...       do_cleanup();
         | 
         | and the other did:                   rc = do_something();
         | if (rc) goto xout;         rc = do_something_else();
         | ...       xout:         do_cleanup();
         | 
         | I think the goto was clearer and harder to get wrong?
        
       | m4r35n357 wrote:
       | You can use static local variables with goto to implement yield
       | semantics in c, look it up!
        
       | beej71 wrote:
       | Oh good, I thought! Maybe there are some ideas here I could add
       | to the book! :)
        
       | 0xTJ wrote:
       | Blanket rules like "never use `goto`" are generally not great. A
       | big part of the job of a software developer is knowing when to
       | use what bits of a language.
       | 
       | Sometimes code with `goto` is simply easier to understand. You
       | have to be careful, but dismissing it completely is throwing out
       | a tool.
        
         | TheCapn wrote:
         | It's a rule given to those still learning the craft. Some
         | people just don't realize that once you gain a level of
         | expertise you _should_ question the rules you were given and
         | make educated decisions on whether those rules were just guard
         | rails to help you learn or actual constraints put in for a real
         | purpose.
         | 
         | When I was early on in code I abused the GOTO, once I learned
         | it was taboo, I was forced to learn how to structure
         | functions/statements in a way that flowed smoothly and created
         | readability in the code that wasn't there before. Now that I
         | understand the pitfalls, I use goto primarily in the "Bailing
         | Out" form described int he link. To try and force the code to
         | do what it does without the goto would likely create a bigger
         | mess as I'd just be nesting statements beyond ever growing if-
         | else branches designed to kick out when failure conditions
         | occurred.
        
         | mikevm wrote:
         | People who give blanket rules such as never to use `goto`
         | clearly do not understand where this idea comes from or why it
         | should not be used. Many years ago in my undergrad I submitted
         | a programming assignment in C which used `goto` for cleanup, as
         | is customarily done in the Linux Kernel and other C software
         | and I had points taken off for using `goto`. Heh.
        
           | SAI_Peregrinus wrote:
           | Should've used setjmp/longjmp to do the same thing. Make it
           | even harder to reason about.
        
           | munificent wrote:
           | _> People who give blanket rules such as never to use `goto`
           | clearly do not understand where this idea comes from or why
           | it should not be used._
           | 
           | The idea comes from Dijkstra and he sure as hell did consider
           | it a blanket rule: "I became convinced that the go to
           | statement should be abolished from all 'higher level'
           | programming languages (i.e. everything except, perhaps,
           | machine code)."
        
             | bgorman wrote:
             | C is portable machine code :)
        
               | munificent wrote:
               | Not in Dijkstra's time it wasn't.
        
               | krapp wrote:
               | On any modern system it really isn't[0,1].
               | 
               | [0]https://news.ycombinator.com/item?id=16967675
               | 
               | [1]https://queue.acm.org/detail.cfm?id=3212479
        
         | atq2119 wrote:
         | A lot of software engineering techniques are more about
         | managing mediocrity than fostering excellence. They go
         | overboard with the prescriptions. This may well produce better
         | results when you have an army of mediocre developers.
         | 
         | It tends to frustrate excellent developers though, so it's a
         | tricky weapon to wield.
         | 
         | Of course, it also tends to frustrate those who only think
         | they're excellent.
        
       | jll29 wrote:
       | I like the presentation of the three nexted loops.
       | 
       | Maybe "break;" should become a shortform for a generalized
       | "break(1);" (= break one level) if we want to avoid using goto.
       | 
       | Perhaps this can be implemented by combining a macro with
       | setjmmp() and longjmp() from the standard C library, without
       | requiring a compiler change.
        
         | wruza wrote:
         | Yep, some goto uses were ostracized without ever providing the
         | alternative. In other languages breaking numerous loops is done
         | by naming the one you want to control:                 outer:
         | for () {         for () {           for () {             break
         | outer;
        
           | OskarS wrote:
           | This is much more confusing to me than just using a goto.
           | From your example, it's not immediately clear at all to me if
           | you're breaking out of outer, or if you're breaking TO outer.
           | You can figure it out with this simple example, but it's way
           | more confusing than it needs to be. And importantly: way more
           | confusing than just using goto.
           | 
           | Given that break and continue are just gussied up gotos
           | anyway, just put the label where you wanna go and goto it. I
           | think it's one of the few perfectly valid uses goto.
        
             | Jtsummers wrote:
             | There is no confusion if you're already familiar with the
             | anonymous break:                 // find in a loop
             | for (a : array) {         if (a == target) {
             | print("Found it");           break;         }       }
             | 
             | This breaks the loop. A named break is no different except
             | that it names the loop that will be broken out of:
             | find:       for (a : array) {         if (a == target) {
             | print("Found it");           break find;         }       }
             | 
             | Exactly the same as the previous, but we've named the loop
             | for some reason. If you can understand this, then the case
             | of named breaks (or named continues) with multiple nested
             | loops are comprehensible with some effort spent writing or
             | reading illustrative examples like the above. Why would it
             | do anything else? A break is a break, it terminates the
             | loop. To re-enter that loop would be a continue, not a
             | break.
             | 
             | > Given that break and continue are just gussied up gotos
             | anyway, just put the label where you wanna go and goto it.
             | I think it's one of the few perfectly valid uses goto.
             | 
             | The reason not to do this is that goto's can (as
             | illustrated in the submitted article) lead to some
             | erroneous behavior that is _harder or impossible_ to
             | achieve with more structured equivalents (like named breaks
             | and continues). Like, your goto can jump past variable
             | initializations and get you undefined behavior. Using the
             | structured equivalent of:                 for (a : array) {
             | if (a == target) {           print ("Found it");
             | goto found_it;         }       }       found_it: ...
             | 
             | Removes those potential errors from the system. That, of
             | course, doesn't mean that goto should be forbidden entirely
             | (plenty of examples in this discussion of where it's
             | useful), or that something like the preceding wouldn't be
             | useful in some circumstance. But if we follow the idea that
             | you present ("break and continue are just gussied up gotos
             | anyway") to its conclusion, we'd be back to the
             | unstructured code that was Fortran and its contemporaries,
             | because why should we have if, if-else, while, for,
             | function calls, etc. when they're _all_ just gussied up
             | goto anyways?
        
               | rightbyte wrote:
               | The label should be at the end of the for block for no
               | confusion. It just looks like the whole thing will be run
               | again.
        
               | Jtsummers wrote:
               | If you expect break to go to the start of a loop, then
               | you've confused it with continue.
        
           | jkcxn wrote:
           | Isn't it interesting that all of the examples in the original
           | article can be replaced with a labeled break of arbitrary
           | blocks? Even replacing the continue statement. Solving the
           | problems of goto and keeping the behaviour. Not sure why more
           | languages don't have this
        
         | beej71 wrote:
         | The next version of C is likely going to have `break break` to
         | break two levels. I'd have preferred `break label` or no
         | change, but there we are.
        
       | buescher wrote:
       | For those that haven't seen Knuth's "Structured Programming with
       | go to Statements" before: https://pic.plover.com/knuth-GOTO.pdf
       | 
       | Enjoy!
        
       | tyingq wrote:
       | Perl has goto, but it also supports labels you can apply to
       | loops, and call for redo, next and last. Where "next" is like
       | "continue" and "last" is like "break".
        
       | jugg1es wrote:
       | The utility of goto is one of those things I've always understood
       | but been too embarrassed to say out loud.
        
       | t43562 wrote:
       | I find that using goto for cleanups makes many functions much
       | much clearer and I think it cuts bugs.
       | 
       | Rather than writing the cleanup that is appropriate for each
       | particular exit point you write one big cleanup at the end of the
       | function that deals with everything.
       | 
       | It adds some potentially unnecessary if statements (to see if
       | some pointer was allocated or not before trying to free it or to
       | see if some file handle was opened before closing it). To me it
       | has always seemed well worth it - shortening the code and greatly
       | simplifying the chains of error-handling if statements.
        
       | marcodiego wrote:
       | Actually "GOTO Considered Harmful" should be considered harmful.
       | But goto should but not used by beginners, or else they get bad
       | habits. Also, people should consider that there is a proposal for
       | "break break" in C2X: http://www.open-
       | std.org/jtc1/sc22/wg14/www/docs/n2859.pdf
        
       | GuB-42 wrote:
       | I think we must distinguish between forward and backwards goto.
       | And whether or not you are entering blocks.
       | 
       | If you are not entering blocks, forward goto is usually fine, and
       | for me, there is no good reason to think is is worse than break,
       | continue and return. In fact, I would more readily ban mid-
       | function return than this kind of goto.
       | 
       | Backwards goto is when you get spaghetti code, there is some use
       | for it, but it is very easy to make a mess.
       | 
       | Entering blocks is particularly bad because it breaks both flow
       | and initialization, I think if there is one kind of goto that
       | should be considered harmful, that's the one, these can even
       | break compilers. But as always, if there is a good reason to do
       | it, why not, but you need a lot of convincing.
        
         | garenp wrote:
         | This is generally my sentiment as well. The reason to avoid
         | using goto is if it makes following the flow of execution for
         | us humans difficult, which typically happens when jumping
         | backwards.
         | 
         | Using goto judiciously to more easily escape a deeply nested
         | block of code and/or jump to a cleanup section is not hard to
         | follow.
        
       | billpg wrote:
       | My favorite use of goto is with C#'s "yield return".
       | 
       | The compiler converts a function with a yield command into a
       | label. To resume a function from where it left off, the compiler
       | adds code to test an added resumption-point variable and to goto
       | the label for each resumption point.
        
         | heavenlyblue wrote:
         | All code that ends up being written as assembly will become a
         | command (an opcode or a set of opcodes) and a label (the
         | opcode's address).
        
         | fjfaase wrote:
         | I have used the goto statement to implement enumerators classes
         | in C++ using a state member and a switch statement with goto
         | statements at the start of the method. Of course, also all
         | local (loop) variables need to be replaced by (private)
         | members. At each 'yield' location, you set the state variable,
         | have a return statement, followed by a label.
         | 
         | It is also possible to do this in C with functions and structs.
         | See for example: https://www.iwriteiam.nl/Ha_cmt.html
        
       | woliveirajr wrote:
       | At StackOverflow you're downvoted heavily when you just typ got
       | (don't event have time for the last "o").
       | 
       | Then Apple comes with the glorious "goto fail" and shows that
       | real companies use goto, and the problem wasn't "goto" itself.
        
         | dleslie wrote:
         | It seemed odd to me that their fail context didn't set 'err',
         | but assumed it would be set by the origin of the jump. At
         | least, I would have initialized 'err' to a failure value, so
         | that any mistake that led it not to be set would result in a
         | valid error state.
        
       | Wildgoose wrote:
       | I remember getting "sniffy" comments for using a GOTO in my final
       | year project, (which was written in Pascal).
       | 
       | Basically, it was used to terminate the program without having to
       | thread a return-value through very heavily nested code.
       | 
       | I haven't needed to use a GOTO since. But almost 35 years later,
       | I still maintain it was the correct decision at the time: a
       | single GOTO statement was the clearest and most efficient choice.
        
       | bsenftner wrote:
       | Many C/C++ UI frameworks require allocating component parts,
       | linking them together to create an operating widget, and then
       | attaching callbacks and whatnot... The code creating a
       | dialog/window/toolbar with multiple such widgets is a perfect
       | case for using goto to implement a single code block of unwinding
       | of the complex construction of that dialog/window/toolbar.
       | Multiple times I've had long winded debates on why complex
       | dynamic memory structure unrolling is safest as a single code
       | block. The subtle win is the unexpected MASSIVE code shrinkage
       | that occurs when a UI's codebase adopts goto for dynamic
       | structure unrolling. I've seen applications that dragged become
       | nimble if not perky after the code reduction from removing 2/3rds
       | of the code that was nothing but repeats of partial structure
       | unrolling due to an error at one spot, and a similar one a few
       | lines down with a complete duplicate of the upper unrolling and a
       | bit more added, and then another duplicate a new lines further,
       | and again and again and again...
        
         | ahartmetz wrote:
         | Maybe so in C, but in C++, stack-allocated container classes,
         | smart pointers, scope guards(!), and (with Qt) the parent-child
         | system can handle almost all cases. Binary size usually
         | increases but lines of code are less or equal to a C-style
         | solution.
         | 
         | It's called RAII, but the main point is really that destructors
         | are automatically called on scope exit.
        
       | DeathArrow wrote:
       | I learned Basic in the 90s and there was no for, no while, no
       | functions or procedures. Just ifs and goto. Its hard to structure
       | a program just with goto and it's even harder to read it and
       | understand the flow.
        
         | [deleted]
        
       | einpoklum wrote:
       | Can't say that I'm enthusiastic about goto advice, but - for a
       | second there, I thought the domain was bejeez.us :-P
        
         | Grazester wrote:
         | When bit banging on a slower microcontroller I have found goto
         | has it's uses.
        
       | kazinator wrote:
       | 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:             switch (getkey()) {             case 3:
       | goto got_3;             case 1:               goto got_1;
       | default:               goto keypad;             }
       | got_3:             switch (getkey()) {             case 4:
       | return 1;             case 1:               goto got_1;
       | default:               goto keypad;             }        }
       | 
       | Now, refactor the same structure into tail calls. Now it's
       | suddenly beyond reproach.                  int got_1();
       | int got_2();        int got_3();             int keypad()
       | {           switch (getkey()) {           case 1:
       | return got_1();           default:             return keypad();
       | }        }             int got_1()        {          switch
       | (getkey()) {          case 1:            return got_1();
       | case 2:            return got_2();          default:
       | return keypad();          }        }             int got_2()
       | {           switch (getkey()) {           case 3:
       | return got_3();           case 1:             return got_1();
       | default:             return keypad();           }        }
       | int got_3()        {           switch (getkey()) {           case
       | 4:             return 1;           case 1:             return
       | got_1();           default:             return keypad();
       | }        }
       | 
       | You can refactor any flow control graph based on goto, with local
       | variables, into a network of tail calling functions. The tail
       | call graph is isomorphic to the goto: it's just as complex and
       | hard to understand. Yet it has the virtue of being beyond the
       | reproach of computer science academia.
        
       | JKCalhoun wrote:
       | > Multi-level Cleanup
       | 
       | Used all the time a decade or so ago when writing code that made
       | heavy use of Apple's CoreFoundation. CF _could_ return nil
       | (failure) for many operations, and memory management at that time
       | was left up to the programmer.
       | 
       | // Not real code, approximated from recollection
       | 
       | bool createNestedCFThing () {                  CFDictionary
       | *thing = nil;        CFArray *arrayObj = nil;        CFString
       | *string1 = nil;        CFString *string0 = nil;
       | string0 = CFCreateString ("Hello");        if (string0 == nil) {
       | goto bail;        }                string1 = CFCreateString
       | ("World");        if (string1 == nil) {           goto bail;
       | }             arrayObj = CFCreateArray ();        if (arrayObj ==
       | nil) {           goto bail;        }             if
       | (CFArrayAddObject (arrayObj, string0) == false) {           goto
       | bail;        }             if (CFArrayAddObject (arrayObj,
       | string1) == false) {           goto bail;        }
       | thing = CFCreateDictionary ();        if (thing == nil) {
       | goto bail;        }             if
       | (!CFDictionaryInsertObjectWithKey (thing, arrayObj, "somekey")) {
       | goto bail;        }
       | 
       | bail:                  if (arrayObj != nil) {           CFRelease
       | (arrayObj);        }             if (string1 != nil) {
       | CFRelease (string1);        }             if (string0 != nil) {
       | CFRelease (string0);        }             return thing;     }
        
         | mkipper wrote:
         | This is the most common form of error handling used in the
         | Linux kernel (at least the parts I look at). Although you'd
         | normally have one error label for each allocation in reverse
         | order, so you can jump to the cleanup step for the most
         | recently allocated resource and pass through the other labels
         | and clean them up as well. So in your example, it would be
         | something like:                 bail_string1:
         | CFRelease(string1);       bail_string0:
         | CFRelease(string0);       bail_array:
         | CFRelease(arrayObj);         no_bail:         return thing;
        
         | vlovich123 wrote:
         | Manual memory management is bad but you can't escape it in C so
         | `goto` is mildly useful there for that purpose. In all other
         | languages you get better ergonomics.
         | 
         | * In Rust you wouldn't need any of that boilerplate because
         | lifetime is automatically managed (& Rust has Objc bindings).
         | 
         | * If you turn on ARC & use the NS types you don't need this
         | (they map to the same thing with no overhead for these
         | primitives, so there's no good reason not to do this unless you
         | are just being masochistic).
         | 
         | * In ObjC++ you should still use ARC. But if you felt
         | masochistic, you could do:                   auto string0 =
         | unique_ptr<CFString, CFRelease>(CFCreateString("Hello"));
         | auto string1 = unique_ptr<CFString,
         | CFRelease>(CFCreateString("World"));         auto arrayObj =
         | unique_ptr<CFArray,
         | CFRelease>(CFCreateMutableArray(kCFAllocatorDefault, 2,
         | kCFTypeArrayCallBacks, kCFTypeDictionaryKeyCallBacks, ));
         | auto thing = unique_ptr<CFDictionary,
         | CFRelease>(CFCreateMutableDictionary(kCFAllocatorDefault, 1,
         | kCFTypeDictionaryKeyCallBacks,
         | kCFTypeDictionaryValueCallBacks));         if (!arrayObj ||
         | !string0 || !string1 || !thing) {           return nil;
         | }         if (!CFArrayAppendValue(arrayObj.get(),
         | string0.get()) || !CFArrayAppendValue(arrayObj.get(),
         | string1.get())) {           return nil;         }         if
         | (!CFDictionaryInsertObjectWithKey(thing.get(), arrayObj.get(),
         | "somekey")) {           return nil;         }         return
         | thing.release()
         | 
         | In C++ you could also use scope guards if you didn't like the
         | `unique_ptr` stuff (not part of the stdlib yet, but it's not
         | hard to roll your own).
         | 
         | The point I'm trying to make is that there are much cleaner
         | ways of obtaining that result in whatever language you choose.
         | If you do that, maybe you too won't be responsible for a goto
         | fail security flaw [1].
         | 
         | EDIT: BTW. This advise isn't out of the blue. This was 100% a
         | strong culture even within Apple 5 years ago. If you're
         | concerned about performance of ARC, consider that Apple
         | dogfoods it internally for nearly every part of the OS. The
         | only pieces it's not used are for large historical codebases
         | where the migration cost is a factor. Performance of ARC vs
         | manual just isn't something anyone looks at. For the historical
         | codebases usually one would use the ObjC++ approach instead.
         | 
         | [1] https://nakedsecurity.sophos.com/2014/02/24/anatomy-of-a-
         | got...
        
         | lmilcin wrote:
         | This is my favorite use of goto. Not only it makes it more
         | readable and easy to ensure the cleanup is only executed, it
         | results in efficient binary representation which is important
         | for some of the embedded stuff I am doing.
        
       | N0tAThr0wAway wrote:
       | Goto is like a tool in your tool shed that may only be used once
       | a year or less. It has an extremely narrow use-case, can be
       | dangerous is used improperly, and may require more experience and
       | training versus using your run of the mill pipe wrench.
       | 
       | I always hated the platitudes that people use when talking about
       | gotos. Yes it definitely can be dangerous, yes it's probably best
       | to refactor a specific area of your codebase to not use them, but
       | every once in a while, it's the right tool for the right job.
        
       | ravenstine wrote:
       | Goto probably doesn't have much of a place in structured
       | programming, but I think there are potential applications for it
       | in more finite domains.
       | 
       | Though I never finished it, years ago I was working on a language
       | specifically designed for voice activated devices, interactive
       | fiction games, and so on. I wasn't happy writing Alexa skills
       | using languages like JavaScript because such languages have more
       | features and complexity than are ever used for a voice interface.
       | So I worked on a lexer-parser for a language I invented that was
       | very minimal.
       | 
       | These were the features of the language:
       | 
       | - No syntactical scoping. You have to be explicit about the "data
       | set" you are using.
       | 
       | - Close to being more like an "assembly" language, every line
       | being a command with parameters, but is human readable and
       | declarative, and supports expressions.
       | 
       | - Control flow is done not only through the equivalent of a
       | "goto", but something like "goto chapter" and "goto section".
       | 
       | - There is no syntactical concept of loops. Loops are effectively
       | performed using gotos and conditions.
       | 
       | - Lines beginning with whitespace are interpreted as text or
       | speech to be rendered.
       | 
       | For the purpose of what I wanted to do, this allowed me to focus
       | more on the user experience and less on fitting my ideas into
       | languages designed to support algorithms, OOP, and so on. The
       | simplicity of using gotos meant less conceptual overhead;
       | everything could be done with just if-else, goto, math operators,
       | and negation.
       | 
       | I imagine goto may also be a good way to introduce young kids to
       | the concept of programming, though I suppose that could backfire
       | if they get too used to writing unstructured programs.
        
       | wruza wrote:
       | Most programmers and I believe even most teachers see technology
       | not as an application, but as some sort of a blind religion.
       | Shallow understanding only adds to this problem. I've seen enough
       | of them saying "what, goto?" in disgust, but almost no one could
       | explain why, apart from saying that it is a taboo and maybe
       | remembering that it was in some book.
       | 
       | They need it, and it's really good that it was "goto". If it
       | wasn't, they would jump on another, more useful bogeyman, like
       | eval.
       | 
       | And for those interested, the specifical use of goto that was
       | _considered harmful_ is its use in place of "for" and
       | "[do-]while" flow control statements and explicit subroutines.
       | Literally:                 loop: ...;       if (!cond) goto loop;
       | a = 42; b = 13; goto sub;       ...       sub: ...
       | 
       | It was used as such by inertia, because these were times before
       | invention/adoption of the structured code.
       | 
       | While you may not want to goto into inner scopes for obvious
       | reasons, leaving two loops or goto-ing to a function cleanup
       | section is perfectly okay and is much clearer than breaking the
       | outer loop with a boolean or building ladders of ifs.
        
       | bigbillheck wrote:
       | Structured programming has won so thoroughly and become so
       | pervasive that it's hard nowadays to understand the context in
       | which Dijkstra was writing.
       | 
       | 'Don't use goto' doesn't mean 'never ever use a goto under any
       | circumstance', it means 'consider using this thing called a
       | "while" loop instead'.
        
         | munificent wrote:
         | _> 'Don't use goto' doesn't mean 'never ever use a goto under
         | any circumstance',_
         | 
         | This is simply not true. When Dijkstra said goto should be
         | considered harmful, he literally meant that _programming
         | languages should not suppport it as a feature at all_.
        
       | ladyattis wrote:
       | I think the issue is only relevant when considering the years
       | when programming languages began to have more nuanced
       | instructions. For Assembly, goto is an essential construct and
       | you'll learn how to use it reliably as jump conditions. For C,
       | it's a great way to get out of nested code that you couldn't
       | avoid for the given problem. For more high level languages,
       | continue and break are the 'goto' statements and they're used all
       | over but they have built in restrictions which make them easy to
       | use. Goto is just hard to master in use for some of us and it's
       | okay to restrict ourselves to limited uses of it. I just don't
       | know why it's become a mantra to say, "do not ever use goto or
       | labels or jump conditions what so ever." It just seems odd to me.
        
         | dec0dedab0de wrote:
         | _For more high level languages, continue and break are the
         | 'goto' statements..._
         | 
         | I have never liked continue or break. It always felt like I
         | messed something up if I found myself needing them.
        
       | vadfa wrote:
       | Like any other tool in the arsenal of a programming language:
       | when it best suits the task.
        
       | hyperman1 wrote:
       | There are a few situations like this, where a bad practice is in
       | a specific case good enough: Using goto to bail out multiple
       | levels, using SHA-1 as a non-secure hash, ...
       | 
       | The problem is these things waste social bandwith: Someone
       | reading your code for maintenance or code review will first
       | declare the code bad (GOTO is ugly! SHA-1 is insecure!), then you
       | have to signal to them that it's actually OK in this case, then
       | they have to convince themselves that actually it is OK.
       | 
       | Hence I only do these things if they are overwhelmingly good.
       | They have not only to be worth it to be written that way, but
       | also be worth it to do the repeated discussion that they are
       | worth it every few months. They deserve a huge comment,
       | containing benchmarks or proof of security or whatever. If the
       | construction is only slightly better, I go with the slightly
       | worse construction.
        
         | simion314 wrote:
         | You can link them to the manual, good languages documentation
         | explain clear and when examples when is better to use GOTO. If
         | there is an asshole in the team the official documentation
         | would calm them down after they will be probably shocked that
         | their cool language implemented this "evil" feature.
        
         | SAI_Peregrinus wrote:
         | > using SHA-1 as a non-secure hash,
         | 
         | That I actually partly disagree with. There are much, MUCH
         | better performing (faster, less memory use, etc) hashes than
         | SHA1 if you don't need security. If you don't need security,
         | _and_ you don 't need performance, and you don't have anything
         | else available in libraries, then SHA1 is fine. But that's a
         | pretty rare situation. SHA1 might not be a sign of insecurity,
         | but it's often a sign of poorly thought out design.
        
           | adrian_b wrote:
           | On modern Intel/AMD and ARM CPUs, which have SHA-1
           | instructions, SHA-1 is very fast, faster than many apparently
           | simpler hashes.
           | 
           | There are no other so fast hashes with an output of at least
           | 128 bits that you can find in available libraries and use
           | immediately in your code.
           | 
           | The only alternative that is faster and long enough is to use
           | an 128-bit polynomial hash like Poly1305 or the one used
           | inside AES-GCM. Such polynomial hashes are available in
           | various cryptographic libraries, but they are not packaged in
           | a way that would allow them to be used directly in a hashing
           | application, so you might have to extract the code from the
           | library and add an interface to it, to make it usable.
           | 
           | Another alternative that has appeared recently is BLAKE3.
           | BLAKE3 can be much faster than SHA-1, but only when it is
           | computed in parallel with a large number of cores. If you do
           | not want your hashing task to entirely take over your
           | computer, SHA-1 remains faster.
           | 
           | There are many applications that need long hashes to make
           | negligible the likelihood of a collision, e.g. for file
           | deduplication. For such applications using SHA-1 is by far
           | the least effort choice and there is no disadvantage in using
           | it.
        
           | hyperman1 wrote:
           | I'm not going to contradict you. This was just one random
           | example, based on the git/SHA-1 story. To stay in this
           | example: If someone is claiming SHA-1 is faster for them, I'd
           | expect to document what other algorithms they tested, what
           | the numbers were, why better things were unavailable. A
           | future maintainer can check if the decision still makes sense
           | based on the comment. Hence the huge comment from the post
           | above.
           | 
           | The problem is, I have a few examples that make a lot more
           | sense, but they can't be published here. They depend on local
           | corporate circumstances and pretty obscure knowledge, and
           | would be undecipherable for the HN crowd.
        
         | leephillips wrote:
         | Julia's approach is to not have GOTO as part of the language
         | syntax, but to have @goto and @label macros. I feel that this
         | strikes a good balance: it sort of discourages their routine
         | use, but they're there if you really need them.
        
         | skipants wrote:
         | > The problem is these things waste social bandwith: Someone
         | reading your code for maintenance or code review will first
         | declare the code bad (GOTO is ugly! SHA-1 is insecure!), then
         | you have to signal to them that it's actually OK in this case,
         | then they have to convince themselves that actually it is OK.
         | 
         | This is something that changes over time as the new convention
         | gets reinforced. If you don't push back it will never change.
         | In other words, the conventional way of doing things needs to
         | be spread organically.
         | 
         | A lot of conventions work that way. Since I'm in Web Dev, one
         | example I can think of is the change from fat models to service
         | objects in MVC. Fat models used to be seen as the way of doing
         | things. But, now that many of us have encountered giant balls
         | of mud caused by this pattern, we've adapted a new convention
         | and moved on.
         | 
         | If you can provide good uses of goto in the wild and push back
         | against opposition the same thing will happen, in my opinion.
         | Eventually the opposition goes away.
        
       | userbinator wrote:
       | Another good use of goto is to implement state machines --- "goto
       | state_x" is clearer and simpler than the loop-switch that people
       | often use.
       | 
       | I can certainly say that over the years I've encountered code
       | which was more difficult to understand and less efficient because
       | either the author didn't want to use goto, or the language didn't
       | have it. While it's true that you can always rewrite code to
       | remove goto statements, it can definitely have a negative effect.
        
         | beej71 wrote:
         | Oh, that's an interesting use I hadn't considered!
        
       | enriquto wrote:
       | Coming from assembly language, I always found the anti-goto
       | sentiment rather cute. A beautiful restriction, but ultimately
       | arbitrary. Like writing poetry. Or those novels that do not ever
       | use the letter "e". Why would an otherwise sane person write code
       | with "rep" and without ever using "jmp"?
        
         | samhw wrote:
         | > Or those novels that do not ever use the letter "e".
         | 
         | As a note on this, I think the novel you're talking about is
         | Georges Perec's 'La Disparition'. Supposedly the lack of an 'e'
         | is symbolic of the loss of his family in the Holocaust. Anyway,
         | I always thought one of the greatest feats of the English
         | language was the guy who translated the novel to English, _also
         | omitting the letter 'e'_. Absolutely mindblowing talent.
        
           | recursive wrote:
           | Also Gadsby by Ernest Wright
        
         | jcranmer wrote:
         | What is done is that people have taken the useful cases of
         | goto, categorized them, and use renamed keywords for different
         | cases. In many modern language, there are four keywords that
         | all imply a general feature of goto: break (go to the end of
         | the identified block), continue (go to the increment block of
         | the identified loop), early return (go to the function cleanup
         | code), and throw (go to something complicated). Indeed, with
         | labeled break and labeled continue, you can actually produce
         | almost any arbitrary control flow [1].
         | 
         | The last major use of goto in C is to recreate what many
         | languages solve via some form of destructor, defer statement,
         | with statement, or some other mechanism that has the compiler
         | insert specified cleanup code on all exiting paths from a
         | block. If you use such a mechanism, then it becomes impossible
         | for the programmer to accidentally skip over such cleanup code,
         | whereas a C function using goto might have the programmer write
         | an early return instead of the goto by accident and skip it.
         | And this is why there's a strong anti-goto sentiment: we have
         | better tools [2] to achieve the same ends that are less error
         | prone than goto is. And when you have the better tools
         | available, why shouldn't you ban the worse tools?
         | 
         | [1] The one thing you can do with goto that you can't do with
         | labeled break/continue is create irreducible control flow
         | graphs (essentially, a loop with two entry points, thus having
         | no dominator within the loop itself). On the other hand, I'm
         | pretty sure that murdering a programmer who creates an
         | irreducible control flow graph is considered justifiable
         | homicide, so no harm is lost by outlawing it.
         | 
         | [2] In languages other than C, though.
        
           | slaymaker1907 wrote:
           | I would disagree with defer being better than goto. Some
           | people prefer it, but I think in a lot of ways it makes your
           | control flow harder to see than just a few cleanup blocks at
           | the end plus goto.
        
             | Jtsummers wrote:
             | It's a tradeoff.
             | 
             |  _defer_ in Go is more like a _try /finally_ statement in
             | other languages, there's a guarantee that the deferred code
             | will run even when there's a panic in the intervening code:
             | handler = open(something);       defer close(handler);
             | // something causing a panic
             | 
             | Should be the same as:                 handler =
             | open(something);       try {         // something causing
             | an exception       } finally {         close(handler);
             | }
             | 
             | This is not necessarily true of goto, you have to be more
             | deliberate and disciplined (and discipline doesn't scale)
             | to ensure that when your code hits an error you go to the
             | cleanup section, if you miss even a single case and return
             | early, you will not do the cleanup. Both defer and finally
             | eliminate that potential error.
             | 
             | The interesting thing (to me) about defer is that it
             | promotes making explicit what's implicit in languages like
             | C++ with RAII. In C++ with RAII, _handler_ would be closed
             | up at the end of the lexical scope but it 's implicit, Go
             | makes you explicitly state that you intend for it to be
             | closed using a defer. But the defer, stylistically not by
             | requirement, being near the initialization is, arguably,
             | clearer than the goto or the finally statement. It's also
             | less error prone, you can scan the function and see that
             | someone left out a _defer close(handler)_. But if you use a
             | cleanup block at the end (either with try /finally or with
             | gotos) you have to jump back and forth between the top and
             | bottom of the function in order to see that everything has
             | been cleaned up and in the correct order.
        
               | slaymaker1907 wrote:
               | There is a huge difference in semantics with defer
               | compared to RAII since defer is called at the end of the
               | function while RAII is scope based. It's mostly the same
               | except for if you want to use RAII in a loop.
        
               | Jtsummers wrote:
               | That's a difference based on the way Go implements defer,
               | but not a principle _of_ defer. A lexically scoped based
               | defer is conceivable.
               | 
               | That said, it's worth noting that difference in practice
               | since Go is, as far as I know, the only mainstream
               | language with defer as a major and commonly used
               | construct.
        
               | rightbyte wrote:
               | I guess Go's "defer" is from "rescue" in Alef (the Plan 9
               | language). Kinda the same guys. But "rescue" was only
               | executed on signals.
        
         | naniwaduni wrote:
         | Goto aversion, like single-entry/single-exit and everything
         | anyone has ever said about 3rd-to-5th-generation programming
         | languages, addresses a concern that hasn't been relevant in
         | decades and that most modern programmers have no context for
         | whatsoever.
         | 
         | The words have straightforward meanings, unfortunately, so
         | they've since been recontextualized into settings where their
         | prescriptions can still be followed, but the rationale doesn't
         | hold up at all.
        
         | IshKebab wrote:
         | It's not at all like that. `goto` leads to hard to follow, bug-
         | prone code.
         | 
         | > Why would an otherwise sane person write code with "rep" and
         | without ever using "jmp"?
         | 
         | They wouldn't. Nobody is suggesting that you should avoid `jmp`
         | in assembly. I think maybe you missed the point? The whole
         | "avoid goto" thing is talking about higher level languages than
         | assembly that provide proper flow control primitives like `if`,
         | `for`, `switch` and so on.
        
           | efaref wrote:
           | Avoiding jmp in assembly _is_ a sensible thing to do. jmp may
           | stall the pipeline if the branch predictor is wrong. Better
           | to use cmov when you can.
           | 
           | And if we're being silly, you only really need mov[1].
           | 
           | [1]: https://github.com/xoreaxeaxeax/movfuscator
        
             | IshKebab wrote:
             | That's a completely separate architecture-specific micro-
             | optimisation.
        
         | mytailorisrich wrote:
         | It's not arbitrary.
         | 
         | The goal is to improve quality by making the code better
         | structured and easier to follow.
         | 
         | For instance, MISRA C rule 15.1 that states that _goto_ should
         | not be used explains:  " _Unconstrained use of goto can lead to
         | programs that are unstructured and extremely difficult to
         | understand_ ". Indeed, in practice that's often the case.
        
           | lelanthran wrote:
           | > For instance, MISRA C rule 15.1 that states that goto
           | should not be used explains: "Unconstrained use of goto can
           | lead to programs that are unstructured and extremely
           | difficult to understand"
           | 
           | Sure, but _constrained_ uses are fine, right? So why blanket-
           | ban it?
        
             | metarox wrote:
             | MISRA doesn't ban it, that rule is only Advisory which
             | pretty much means you can ignore it. Rules 15.2 and 15.3
             | (Required) define how you can use goto if you decide to use
             | it.
             | 
             | Rule 15.2 The goto statement shall jump to a label declared
             | later in the same function (forward gotos only)
             | 
             | Rule 15.3 Any label referenced by a goto statement shall be
             | declared in the same block, or in any block enclosing the
             | goto statement (can't jump a scope level lower than current
             | [jumping in an if block from outside] or from 2 identical
             | but separate scopes [from one if to another independent
             | if])
             | 
             | Personally, I like the Linux kernel model for goto which
             | fits these constraints. Reading code like a shopping list
             | and jumping to the proper cleanup point is much easier to
             | read than having 3-4 if else levels and having to scroll
             | the screen to make sure resources are cleaned at all
             | levels, etc. Nothing beats making the mental model of code
             | smaller to keep in my head and readability in my book so
             | I'll take that approach over applying holy war-ladden
             | absolute rules.
             | 
             | Note that using gotos also jives well with Rule 15.5
             | requiring a single exit point in functions (Advisory).
             | 
             | Edit: as panax wrote somewhere in this thread, CERT-C rules
             | even recommend it
        
               | lelanthran wrote:
               | I'm familiar with MISRA (embedded dev), I only wanted to
               | point out that the MISRA rules in no way justifies a
               | blanket ban on gotos.
               | 
               | The ban (in the case of C, anyway) is almost always the
               | result of someone's completely subjective opinion that
               | gotos are bad.
        
               | mytailorisrich wrote:
               | It's easier to 'ban' it and then to make exceptions on a
               | case by case basis when not using _goto_ make things
               | worse.
               | 
               | That has three practical benefits: It's clear (which is
               | an underrated quality), it forces people to structure
               | their code accordingly, and it prevents endless
               | discussions (another very valuable quality). Otherwise,
               | in my experience the use of _goto_ tends to creep up and
               | every occurrence leads to discussions in code review.
               | 
               | So it's not that those decisions are subjective is that
               | they are practical and workable. Especially, if you are
               | defining practices for contractors to follow it is
               | beneficial to keep those rules simple and clear as they
               | will form contractual obligations.
        
               | panax wrote:
               | A customer put a blanket ban on goto because they decided
               | to require MISRA 15.1 and want(need) to be compliant as
               | well as having their own interpretation of MISRA. They
               | also put a blanket ban on multiple returns (15.5).
               | 
               | While it is true MISRA does not intend a blanket ban on
               | these (and a blanket ban is contrary to the rationale for
               | these rules), it often leads to a blanket ban and when
               | these two in particular happen together it leads to awful
               | code. I've seen this from multiple customers in different
               | industries.
        
               | metarox wrote:
               | I fully concur on that last statement!
        
             | buescher wrote:
             | At least one of the versions of MISRA explicitly allows for
             | the use of goto to break to cleanup code at the end of a
             | function, if I remember correctly, and another is much more
             | strict, but I don't have any copies of MISRA at hand at the
             | moment.
        
           | zabzonk wrote:
           | Well, in C you very rarely need it, but in assembler
           | (particularly the 8-bit Z80 and 6502 that I've written raft-
           | loads of code for) you definitely do. Of course, something
           | like a CALL is better, if you can manage it.
        
             | sys_64738 wrote:
             | It's mostly used for error bailing as nested if can be
             | pretty dense to read and error prone.
        
             | formerly_proven wrote:
             | goto is the idiomatic way to emulate RAII in C. It's
             | everywhere.
        
               | zabzonk wrote:
               | Please explain a little more with an example. Certainly
               | not in my C code, though I a huge fan of RAII in C++.
        
               | panax wrote:
               | Here is an example with RAII and goto: https://wiki.sei.c
               | mu.edu/confluence/display/c/MEM12-C.+Consi...
               | 
               | Btw that is a Cert C recommendation to use goto for RAII!
               | 
               | Take a look at the Linux kernel sometime, goto is
               | everywhere there too
               | https://www.kernel.org/doc/html/v4.10/process/coding-
               | style.h...
        
               | zabzonk wrote:
               | Not many of us are kernel developers, or agree whole-
               | heartedly with their practices.
        
               | ahartmetz wrote:
               | A function that allocates resources in several steps
               | jumps, on error, to the teardown part at the end to
               | deallocate (in reverse order of allocation) only the
               | resources that were allocated. The others are skipped
               | (jumped over). It's very popular in the Linux kernel.
        
               | zabzonk wrote:
               | > only the resources that were allocated
               | 
               | How does it know which were allocated and which were not?
               | If you are talking about memory allocation, then you
               | could simply deallocate all the pointers provided they
               | were initially set to NULL. Freeing a NULL pointer is
               | well-defined in C.
        
               | ahartmetz wrote:
               | The information is in the instruction pointer. You jump
               | to the place that deallocates the last resource that was
               | allocated, followed by the second-to-last etc.
               | 
               | Also, free(NULL) is _not_ well-defined in C, and other
               | resource types aren 't so easy to check. delete nullptr
               | in C++ _is_ well-defined, but you rarely need it.
        
               | zabzonk wrote:
               | > free(NULL) is not well-defined in C
               | 
               | Oh yes it is:
               | https://stackoverflow.com/questions/1938735/does-freeptr-
               | whe...
        
               | ahartmetz wrote:
               | I stand corrected. So, these days, null checks before
               | free() are just for compatibility with very old systems,
               | or more likely "tradition". I wonder where I got my wrong
               | information? I have even seen null checks before delete
               | in C++ code recently written by C programmers :o
        
               | wott wrote:
               | When it is in the normal execution path, the test avoids
               | a function call, so there's a little bit of performance
               | gain.
               | 
               | It can also be a bit self-documenting, or be a place-
               | holder if there is for example stuff to delete in a loop
               | (elements of an array) before releasing the pointer
               | itself (the array).
               | 
               | I don't know, when you read it, sometimes it feels
               | natural and meaningful, and sometimes you know that the
               | author didn't know that free() handles NULL all right.
        
               | maccard wrote:
               | Files, sockets, mutexes are all examples of resources
               | that aren't memory. Someone posted an example in this
               | comment thread - [0].
               | 
               | [0] https://wiki.sei.cmu.edu/confluence/display/c/MEM12-C
               | .+Consi...
        
               | zabzonk wrote:
               | > Files, sockets, mutexes are all examples of resources
               | that aren't memory
               | 
               | I know - that's why I explicitly mentioned memory.
        
           | scoutt wrote:
           | I kind-of agree with parent, but I would define it
           | "subjective" instead of "arbitrary".
        
             | mytailorisrich wrote:
             | Well, as per my previous comment it's not subjective,
             | either.
             | 
             | It's a "best practice" in the correct sense of the term.
             | I.e. in most cases it is both not needed and clearer, more
             | structured code may be written without using it.
             | 
             | Of course, as for anything, it is useful in a very few
             | cases, emphasis on "very few".
        
               | scoutt wrote:
               | > improve quality by making the code better structured
               | and easier to follow
               | 
               | "clearer" and "structured" doesn't matter from a compiler
               | point of view. Hence the only ones that can judge
               | "clearer" or "structured" is us, human beings, and that's
               | why I think it's subjective.
               | 
               | "goto" is seen as "incorrect" only because of mere
               | subjective reasons: "it's not clear", "it's not
               | structured", "I read in a forum that it's bad", etc.
               | 
               | The keyword exists, it works as intended, and it's safe
               | to use when used correctly, as all the things around
               | programming languages.
               | 
               | To me, Rust code is a mess and I cannot get used to it.
               | It's not "clearer" even if it's proven to be bug free and
               | all that.
        
               | dnautics wrote:
               | Rust is not proven to be bug free, it's proven to be
               | memory-safe. Rusts compiler will not save you from bugs
               | in business logic; if you can't write lucid rust and the
               | gunning fog of reviewing your code makes it harder to
               | spot a bug it might not be for you.
        
           | mysterydip wrote:
           | Isn't the key word in that statement "unconstrained"? Goto is
           | a tool like any other that can be abused.
        
             | mytailorisrich wrote:
             | That's why there is this rule to constrain it!
             | 
             | It is pragmatic, practical, and clear to have a rule not to
             | use it. This forces people to make the effort to structure
             | their code. Now, if it so happens one day that the result
             | is indeed worse than using _goto_ then a conscious decision
             | can be made to make a specific exception. Otherwise, this
             | is the sort of thing that will creep up and cause unless
             | discussions.
        
           | enriquto wrote:
           | I agree with you; and I keep a printed copy of the MISRA C
           | document on my nightstand for serene reading before sleep
           | (together with the JPL C rules). Yet this point 15.1 does not
           | forbid _at all_ to use goto, just to use goto in an
           | unconstrained way. Thus it is more a warning than a rule.
           | 
           | In the same spirit, you can also enjoy Knuth's "structured
           | programming with goto statements" for a clean programming
           | style whose control flow is based on goto.
        
             | panax wrote:
             | And yet we were recently forced to change a lot of code
             | which did use goto in a constrained way(for error handling
             | only), because someone decided to make 15.1 required
             | instead of advisory and take 15.1 too literally (they also
             | made 15.5 no multiple returns required as well) and some of
             | the results were worse than if someone had used goto in an
             | unconstrained way.
        
         | mumblemumble wrote:
         | > A beautiful restriction, but ultimately arbitrary. Like
         | writing poetry.
         | 
         | This assessment cuts pretty close to the gist of (my reading
         | of) Dijkstra's famous paper that kicked off the anti-goto
         | sentiment, once you take some time to digest the entirety of
         | the paper and consider the context in which it was being
         | written.
         | 
         | I'd also like to throw out there, though, that the "go to"
         | statement he describes is a "go to" statement that functions
         | like the one in programming languages from before 1968. Which
         | is a goto statement that, I'm guessing, very few of us have
         | ever used. Early versions of BASIC - the "20 GOTO 10" variety -
         | are maybe the most well-known example nowadays. If you've spent
         | much time with a Commodore 64 or a 1st-generation IBM PC,
         | you're probably familiar.
         | 
         | He wasn't criticizing C's goto. Not just because C hadn't been
         | invented yet. His most lucid, damning criticisms of goto in
         | higher-level languages can't really be leveled against C's goto
         | statement, because C's goto simply doesn't exhibit the features
         | he's criticizing.
        
           | jsmith45 wrote:
           | Right. He was criticizing the version where one subroutine
           | might jump into the middle of another subroutine.
           | 
           | That sort of thing can make safely modifying the second
           | subroutine require knowing if any of its labels are used
           | elsewhere in the program.
           | 
           | It is basically impossible to reason locally when developing
           | like that, unless you first verify that the only uses are
           | local. (Or have applied strict coding standards such that
           | only labels specifically identified can be used as non-local
           | goto targets, etc.)
           | 
           | That sort of thing was extremely normal in say early assembly
           | programming where your code size was so restricted, you
           | needed to reuse code whenever possible. And many early
           | languages brought that sort of capability along with it.
           | 
           | I'd expect Dijkstra's feeling on C's goto would probably be
           | like: Probably a good idea to avoid it when the other control
           | structures work well, but barring silliness like trying to
           | code a large program in a single function, using C's goto for
           | cases not well handled by the other control structures is
           | fine, since it is constrained to local use only.
        
             | pajko wrote:
             | There's setjmp() and longjmp()to do that :)
        
               | marcan_42 wrote:
               | setjmp() and longjmp() only work upwards through the call
               | stack. They're basically an awkward version of
               | exceptions. You can't jump _into_ a subroutine that isn
               | 't already executing with them.
        
         | userbinator wrote:
         | I started with Asm too and probably have much the same thoughts
         | on goto --- HLLs provide if/else and loops to simplify code, so
         | use them when it makes sense, but when the control flow is such
         | that the simplest solution doesn't fit within the constraints
         | of HLL flow structures, then goto makes perfect sense.
         | 
         | Many of those who started with HLLs probably don't realise the
         | machine can be far more flexible and powerful beyond their
         | constraints. It's all about using the appropriate level of
         | abstraction.
        
         | amelius wrote:
         | Goto can, conceptually, really mess with the semantics of
         | initialization and finalization of local variables.
        
           | zabzonk wrote:
           | Indeed, which is why C++ forbids (and the compiler checks)
           | such jumps.
        
         | da_big_ghey wrote:
         | It is some good point that goto has many helpful uses, it is
         | when people use it for working around when control flow should
         | be re-factored instead that it becomes harmful. Like on many
         | other things it is no issue in moderation, only in excess.
        
         | throw_m239339 wrote:
         | > Coming from assembly language, I always found the anti-goto
         | sentiment rather cute. A beautiful restriction, but ultimately
         | arbitrary. Like writing poetry. Or those novels that do not
         | ever use the letter "e". Why would an otherwise sane person
         | write code with "rep" and without ever using "jmp"?
         | 
         | There is no need to use GOTO when a language has
         | functions/procedures and exceptions.
         | 
         | Of course it makes sense in Assembly or more rudimentary
         | languages, or in specific cases for speed optimization (getting
         | out of multiple loops).
         | 
         | However, I must admit, sometimes I'm sick of the way Go "deals"
         | with errors so I resort to GOTO to manage HTTP errors in my
         | HTTP request handlers.
        
           | msla wrote:
           | > There is no need to use GOTO when a language has
           | functions/procedures and exceptions.
           | 
           | The usual example of when goto saves more trouble than it
           | causes is breaking out of a nested loop early. You _can_
           | avoid goto in that scenario with a sufficient number of
           | flags, but flag-heavy code tends to be harder to read than a
           | couple apposite gotos. Even better is Perl-style named
           | blocks, though.
        
           | mumblemumble wrote:
           | One thing a C-like goto has over exceptions is that you can
           | only jump around within a single lexical scope. That
           | restriction is a big win for readability.
           | 
           | Exceptions use a dynamic scope to decide where to jump to,
           | so, in the general case, it's a lot harder to understand how
           | they will affect program behavior by simply reading the code.
           | That's a major reason why using exceptions for non-
           | exceptional control flow is widely considered to be evil.
        
           | srott wrote:
           | > There is no need to use GOTO when a language has
           | functions/procedures and exceptions.
           | 
           | ... and defer
        
             | dragonwriter wrote:
             | > and defer
             | 
             | Which "defer"? The go panic/defer pair is, while structured
             | differently, functionally equivalent to exceptions.
        
               | 0xFACEFEED wrote:
               | You use exceptions to clean up resources even when there
               | are no errors? Why?
        
               | dragonwriter wrote:
               | > You use exceptions to clean up resources even when
               | there are no errors?
               | 
               | exception handling mechanisms (try/except/finally) are
               | idiomatic for that in languages with them (try/finally,
               | particularly, because it assures cleanup whether or not
               | an unhandled exception occurred); defer offers equivalent
               | power with slightly different structure; you don't need
               | both for any purpose either will do.
        
               | Jtsummers wrote:
               | You don't need to use exceptions for that, but a
               | _finally_ block, something like this:                 try
               | { ... }       catch (SomeException e) { ... }       catch
               | (SomeOtherException e) { ... }       finally { /* cleanup
               | code */ }
               | 
               | Pretty much every language with exceptions has an
               | equivalent of that finally block, a portion of code
               | guaranteed to run whether there is an exception or not.
               | 
               | Defers in Go act like that finally block, but permit you
               | to attach them at the point where the initialization they
               | clean up was written. This can help with longer code
               | blocks, but it's fundamentally the same as the above
               | (with or without the catches). So if you use defer (the
               | following is not really Go, but captures the idea):
               | function echo_file (filename) {         f =
               | open(filename);         if nil == f { return; } // simple
               | error handling, just don't do anything         defer
               | close(f);         for line in f {           print(line);
               | }       }            function echo_file (filename) {
               | f = open(filename);         if nil == f { return; }
               | try {           for line in f {             print(line);
               | }         }         finally {           close(f);
               | }       }
               | 
               | The former is much shorter and clearer, especially for
               | straightforward cleanup code, but is the same as the
               | latter in what it does. Whether an exception or panic
               | occurs during the read/print loop or not, the file will
               | always be closed in both cases.
        
           | lmilcin wrote:
           | > There is no need to use GOTO when a language has
           | functions/procedures and exceptions.
           | 
           | There is no need for exceptions either. Everything can be
           | done with error code.
           | 
           | Just because there are redundant mechanisms to achieve same
           | result does not mean one of them must be abolished.
        
         | Symmetry wrote:
         | Likewise using local variables instead of global variables. The
         | point of structured programming is that it's less flexible than
         | gotos are. Decreasing the flexibility of programming constructs
         | means that the reader has fewer possibilities to consider and
         | hence makes the result easier to reason about.
         | 
         | Beyond structured programming you might have object oriented
         | programming that uses restricted access to the object's members
         | to enforce certain parities on them that make the object's
         | behavior easier to reason about. Or functional programs that
         | restrict the persistent mutable state and make programs easier
         | to reason about in another way.
        
         | heavenlyblue wrote:
         | Even in assembly there's position-dependent and position-
         | independent code.
        
           | wruza wrote:
           | And jumps relative to cs or ip, which serve both. PIC idea is
           | not related to goto.
        
         | kristopolous wrote:
         | Bringing it more generally, it's a powerful tool that's simple
         | to use.
         | 
         | This means it is ripe for abuse by amateurs and fools and from
         | that comes its reputation.
         | 
         | It's a shame that dogmas are developed against the use of such
         | things instead of creating a culture of appropriate use.
         | 
         | You see it against say php, jquery, and even gof design
         | patterns these days.
         | 
         | It's bullshit. Just because some buffoons do stupid things with
         | something powerful it doesn't mean all people that use it are
         | buffoons
        
       | [deleted]
        
       | ainar-g wrote:
       | The site seems to be hugged to death, at least to me. Archive
       | link:
       | https://web.archive.org/web/20211021120233/https://beej.us/g....
        
       | gwbas1c wrote:
       | In the past 20 years I've used a goto a few times, and then
       | refactored it out once I was able to look at the problem with a
       | clear head. I remember that, every time, I replaced the goto by
       | breaking up a large method into smaller methods, and then
       | replacing the goto with either return statements or logic that
       | would essentially return.
       | 
       | Yes, a goto is part of our programmers' toolbox. But, now even I
       | consider a "good goto" a sign that a larger method should be
       | broken into smaller ones.
        
         | recursive wrote:
         | Often this is true, but those smaller methods might need to
         | share so much local state that the solution is worse than the
         | problem.
        
           | Jtsummers wrote:
           | This is where languages that permit nested functions come in
           | handy. You can locally factor out new functions while
           | preserving access to variables in the lexical scope without
           | needing to pass them in as parameters or lift them into a
           | higher level scope (object, class, global, file, etc.).
        
             | recursive wrote:
             | That's a good solution. The next problem is that many code
             | patterns, such as state machines, if naively converted from
             | `goto` to `call()` will consume a lot of unnecessary stack
             | space. This might not be a problem with a language/compiler
             | that supports tail call optimization.
        
               | Jtsummers wrote:
               | Right. Absent TCE and inline functions (the latter is now
               | pretty much bog standard in every language in popular
               | use, at least every compiled language), goto for state
               | machines and similar uses can be much more efficient and
               | also handle the concern of blowing up your stack. If you
               | have TCE and inline functions, then mutual recursion is a
               | perfectly efficient way to handle that kind of situation
               | that is often (but not always) clearer than goto. And if
               | you pair that with nested functions so that you can close
               | over some common lexical scope, you eliminate the need to
               | use global variables or to thread data through each
               | function call (keeping your parameters to a minimum).
        
               | gwbas1c wrote:
               | Perhaps that's true when you're writing performance-
               | critical code.
               | 
               | For most code, readability trumps micro-optimizations.
        
               | recursive wrote:
               | 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 transitions.
               | 
               | Crash vs not-crash is not a micro-optimization.
        
           | munificent wrote:
           | I tend to find that to be itself a code smell. But it
           | happens. When it does, I like using local functions that just
           | close over the same state.
        
       ___________________________________________________________________
       (page generated 2021-10-21 23:01 UTC)