[HN Gopher] GOTO (2000)
       ___________________________________________________________________
        
       GOTO (2000)
        
       Author : luu
       Score  : 32 points
       Date   : 2024-04-15 07:41 UTC (1 days ago)
        
 (HTM) web link (www.azillionmonkeys.com)
 (TXT) w3m dump (www.azillionmonkeys.com)
        
       | whitten wrote:
       | Does this mean that compilers have given up on processing GOTO if
       | it is written directly by a person, but are fine in analyzing
       | code generated in Intermediate code from GOTO-less code ?
        
         | bregma wrote:
         | Not at all. The compilers I'm familiar with (and my dayjob is
         | maintaining compilers for a commercial OS) all just emit a phi
         | node and construct basic blocks for all flow control
         | constructs. By the time you're deep in the IR you have no idea
         | if it was a do-loop, a for-loop, a while loop, a loop with
         | continue and break statements, a series of deeply-nested if-
         | statements, and aggregate initializer, or a goto.
         | 
         | The people who religiously avoid the use of gotos are more
         | often than not just cargo-culters.
        
         | Findecanor wrote:
         | On the contrary. Many passes in a compiler are simpler if the
         | control-flow graph is _reducible_ [1], which it is guaranteed
         | to be if the program had been written in structured programming
         | without any gotos.
         | 
         | It is just that "goto" is closer to assembly code, and
         | therefore used in intermediary code representations inside the
         | compiler for that reason. But if the compiler had made sure
         | beforehand that the control flow is reducible (and don't break
         | that in an intermediary pass), then it will remain so.
         | 
         | I find that most uses of gotos in source code are still
         | reducible control-flow, but which had just not been expressible
         | using structured programming constructs in the language. For
         | example jumping out of nested loops, "for-else" and error
         | handling. It is very rare that you see "spaghetti code" in
         | practice.
         | 
         | 1: https://en.wikipedia.org/wiki/Control-
         | flow_graph#Reducibilit...
        
       | justadolphinnn wrote:
       | GOTO users are holding society back
        
       | peterhull90 wrote:
       | In my youth I had a ZX spectrum which had BASIC with line numbers
       | and no renumber command. Sometimes when adding code I'd simply
       | run out of line numbers so had to GO TO an unused block of line
       | numbers, put the new code there and GO TO just after the original
       | code. I've never quite recovered from that.
        
         | bregma wrote:
         | My first job out of university was maintaining a FORTRAN IV
         | program on a PDP-11. The only control structures in that
         | language was IF..GOTO and the arithmetic GOTO. You can still
         | write readable half-decent code with that, with discipline. A
         | DO loop is still superior for readability.
         | 
         | The horrors of BASIC with its spaghetti of GO TO or its mess of
         | PEEKs and POKEs are a justification for permabanning that style
         | of programming -- but a decade of typing in listings from
         | magazines inspired the generation the brought us the web and
         | pocket phones. Maybe it wasn't such a bad thing after all.
        
           | pklausler wrote:
           | You had a FORTRAN compiler that didn't have DO loops? I'm
           | dubious.
        
         | mmaniac wrote:
         | Reminds me of ROM hacking. Overwriting an instruction inside a
         | function with a branch to unused memory and jumping back later
         | is an easy way to get extra space for the patch you want to
         | write.
        
         | sdwr wrote:
         | Reminds me of those logic games that operate in (virtual)
         | physical space
         | 
         | https://store.steampowered.com/app/300570/Infinifactory/
         | 
         | The feeling being squished into a corner by your own rat's nest
         | is really something.
        
       | boomlinde wrote:
       | Good "Go To Statement Considered Harmful" considered harmful
       | take. I frequently use goto in C to jump to resource cleanup code
       | in case of an error. Always turns out more readable than the
       | alternatives.
       | 
       | I think I may even have used it in Go once, but I can't remember
       | what for. Go's defer keyword obviates the above use of goto, and
       | labeled breaks almost any other case I can think of.
        
         | buescher wrote:
         | I wouldn't die on that hill but I agree and have done the same.
         | In fact I would argue that if you find yourself in arguments
         | about the practice it is a good sign your team is at a level
         | where you should in fact prohibit all use of goto.
         | 
         | IIRC at least one of the revisions of MISRA specifically allows
         | for using goto to break forward to a cleanup-and-return block,
         | but others don't.
        
         | JonChesterfield wrote:
         | You might find splitting the functions into two works well. The
         | outer one tries to allocate whatever resources are needed, the
         | inner one assumes they have been allocated. Ends up looking
         | something like (copied from one of my C codebases)
         | static regex_compare_t
         | regex_canonical_equivalent_with_structures(
         | regex_cache_t *cache, stringtable_index_t x,
         | stringtable_index_t y,           intstack_t *stack_arg,
         | intmap_t *map_arg) {         // fairly complicated code using
         | those structures       }              regex_compare_t
         | regex_canonical_equivalent(regex_cache_t *cache,
         | stringtable_index_t x,
         | stringtable_index_t y) {                if (x.value == y.value)
         | {           return regex_compare_equal;         }
         | intstack_t stack = intstack_create(256);         if
         | (!intstack_valid(stack)) {           return
         | regex_compare_out_of_memory;         }                intmap_t
         | map = intmap_create(16);         if (!intmap_valid(map)) {
         | intstack_destroy(stack);           return
         | regex_compare_out_of_memory;         }
         | regex_compare_t res =
         | regex_canonical_equivalent_with_structures(cache, x, y, &stack,
         | &map);         intstack_destroy(stack);
         | intmap_destroy(map);                return res;       }
        
       | kevindamm wrote:
       | I disagree about the examples of when a go-to is useful:
       | 
       | > - Using a single goto and single label to exit several levels
       | of scope.
       | 
       | Better handled by putting the nested scope in a function and
       | using `return`.
       | 
       | > - Using a goto in the middle of complex construction to short
       | cut to the top of a loop.
       | 
       | Use `continue`.
       | 
       | > - Starting a highly optimized do { ... } while loop that is
       | best initiated by jumping into the center first. (Because your
       | compiler is not cooperating.)
       | 
       | This one's tricky, first be sure that the optimization is
       | actually needed. Then, with a quick glance at DRY for apology put
       | a copy of the second half of the loop before the `do`.
       | 
       | > - Extensions of duff's device.
       | 
       | Oh come on. The original Duff's device is not intuitive when
       | first approaching it, and there is pretty much one use for it. I
       | wouldn't even suggest to juniors that they be aware of this trick
       | at all, tbh. It's a curiosity but I balk at it being a
       | justification for adding gotos to make your own custom extension
       | of it. See above, check that you really need such a severe
       | optimization. You likely don't.
        
         | OskarS wrote:
         | >> - Using a single goto and single label to exit several
         | levels of scope.
         | 
         | > Better handled by putting the nested scope in a function and
         | using `return`.
         | 
         | I dunno, that seems pretty silly to me. It's a perfectly valid
         | use case of goto to jump out of deeply nested loops, and
         | refactoring it to break out the inner loop in a function seems
         | like pointless busy-work that ultimately makes the code harder
         | to read, just so you can avoid a goto.
        
           | clauderoux wrote:
           | @OskarS I agree with you... In fact, I have been avoiding
           | gotos as a matter of principle for years and now reading the
           | different discussions, I think I was misled. This case is
           | very, very common. You have 3 intertwined loops and you want
           | to get out. The traditional solution is to add some
           | intermediate variables to propagate the end of the loops,
           | with a cascade "if (toto) break;" which are far from being
           | exquisitely elegant... Using a function for that is really
           | adding some more complexity and some more stuff on the stack
           | for nothing.
        
             | clauderoux wrote:
             | Also as a matter of technology, using "if" to get out of
             | loops is bad with modern processors and their internal
             | predictive algos.
             | 
             | My solution for a long time was to add the test within the
             | "for" itself, but it is often confusing for users.
        
             | OskarS wrote:
             | Exactly. The only reason not to use goto here is because of
             | a dogmatic opposition to ever using goto. In fact, this is
             | a rare case where goto makes the flow of the program more
             | clear rather than less clear, and alternate solutions are
             | just worse.
        
         | bazoom42 wrote:
         | > put a copy of the second half of the loop before the `do`
         | 
         | But why is that better than just using a goto? Goto's are
         | usually discouraged because they make code hard to follow, but
         | if avoiding the goto makes the code even more convoluted, whats
         | the point?
        
       | OskarS wrote:
       | I've been working my way through the latest volume of The Art of
       | Computer Programming (Volume 4B, released last year) which is
       | about combinatorial algorithms. One of the very first algorithms
       | he presents Algorithm B, "Basic backtrack". It's like a generic
       | backtracking algorithm that you could use to solve combinatorial
       | problems (e.g. n-queens, things like that).
       | 
       | Every time I've seen this algorithm presented, it's used
       | recursion, because that's a very natural way to express
       | backtracking. But Knuth doesn't, he basically never uses
       | recursion in his TAoCP algorithms, he expresses his algorithms in
       | a "flow chart" style, not assuming things like stacks or
       | subroutines (in fact, one of the exercises in the chapter is to
       | convert Algorithm B to a recursive version, which he then quickly
       | dismisses in the answer to the exercise as "elegant, and works
       | find for small problems" but fundamentally not very high
       | performance and not really clearer than the non-recursive
       | version).
       | 
       | So, I set down to implement Algorithm B in a generic way and used
       | it to solve n-queens. The way Knuth has written it, it can be
       | straightforwardly translated using `goto`s (it has lines like "if
       | so-and-so, go to step B4, else B3"), but being the modern,
       | structured programmer that I am, i tried to convert it to
       | structured programming using loops and if statements.
       | 
       | It was doable, but when benchmarking, my versions were always
       | slightly slower than Knuth's original "goto" formulation, and
       | honestly I don't think they were more readable either. You had to
       | add a bunch of variables to keep some state around that was
       | implicit in Knuth's version. The recursive version was definitely
       | more readable, but Knuth was of course correct, you pay a
       | performance penalty for that readability.
       | 
       | It was a real eye-opener. In the hands of a master, "goto"s
       | really do have a place, and when well-deployed, they can
       | sometimes simply be superior to "structured" code using loops,
       | ifs and recursion.
        
         | afiori wrote:
         | One of the arguments un favour if Tail Calls Elimination it
         | that it allows to implement a goto-state-machine with mutally
         | recursive functions.
        
           | OskarS wrote:
           | Yeah, this is a really interesting point, I should try this
           | again with [[musttail]] on clang.
        
       | mrkeen wrote:
       | I think the 'debate' only makes sense in a sufficiently low-level
       | language, at which point you're already taking safety into your
       | own hands.
       | 
       | In a higher-level language, it's as silly as asking if you should
       | avoid explicit malloc/free when writing SQL.
       | 
       | How would the Rust compiler deal with goto? You could dodge
       | ownership and start operating on data before/after it's
       | created/deleted.
       | 
       | A try-with-resources/using in Java/C# would no longer offer its
       | guarantees.
       | 
       | How about async blocks in general? Do you just wander into
       | another thread's code (but keep your own stack frame?)
       | 
       | Lambdas? Closures?
       | 
       | People probably
       | 
       | > regurgitate the age old argument against the use of goto
       | 
       | because there's no new arguments for it.
        
         | neonsunset wrote:
         | C# goto does not let you break `using var ...` guarantees or
         | violate scopes of other control flow constructs.
        
       | ChrisMarshallNY wrote:
       | It is really a post about software dogma.
       | 
       | GOTOs are just one of the oldest ones, but these days, for
       | example, we have polymorphism, and OO, in general, and, for some
       | folks, anything that is \\(!$HOLY_MANNA) is bad.
       | 
       | In my work, I tend to mix brand-new techniques with very old ones
       | (probably older than some of the folks reading these words of
       | prose).
       | 
       |  _Writing Solid Code_ was written in 1992. Many of its techniques
       | have been integrated into toolchains, some are outdated, and some
       | are still every bit as valid, today, as they were, then.
       | 
       | I will say that reading it was a watershed, for me.
        
       | JonChesterfield wrote:
       | The goto of goto considered harmful is not the goto of C. Somehow
       | this subtlety seems to be broadly missed.
       | 
       | The goto of Dijkstra jumps to somewhere else in the program.
       | You're somewhere in the call stack and decide to scribble on some
       | registers and jump to some arbitrary location. Like longjmp with
       | the restrictions taken off. As in across function boundaries.
       | When you're writing assembly that works exactly as it it sounds
       | like it would.
       | 
       | The goto of C jumps to somewhere else in the same function with a
       | bunch of compiler warnings if you manage to bypass an
       | initialisation. You can make control flow more complicated within
       | a single function, sure.
       | 
       | Goto in C is not a significant problem. Jumping between different
       | functions in assembly is however legitimately confusing.
       | 
       | This is something of a pet annoyance because various languages or
       | coding standards decide to outlaw the friendly easy goto as it
       | has such a scary reputation. This makes some algorithms much more
       | complicated to express and others inherently slower.
        
       | nyrikki wrote:
       | Note, the common reason people fail to comprehend what Dykstra
       | was explaining, is because people don't realize they are almost
       | universally adopting one of the three major programming
       | paradigms.
       | 
       | While people are familiar with Functional and OOP, most forgot or
       | don't know about structured programming. Because it has proven to
       | be good by default almost universally.
       | 
       | Look at the COBOL ALTER statement or GCC computed GOTO to see
       | what the issues were.
       | 
       | While there are Bohm-Jacopini theorem purists that argue break
       | and return, it is the law of diminishing returns.
       | 
       | Break and return may be syntactic sugar for GOTO, but they don't
       | have the same problem of the unrestricted transfer of control
       | that was and is considered harmful.
        
       ___________________________________________________________________
       (page generated 2024-04-16 23:01 UTC)