Goto Is Not a Horror
jerf.org
jerf.org
> I think goto's are fine, and they are often more readable than large amounts of indentation. That's _especially_ true if the code flow isn't actually naturally indented (in this case it is, so I don't think using goto is in any way _clearer_ than not, but in general goto's can be quite good for readability).
[1]: https://koblents.com/Ches/Links/Month-Mar-2013/20-Using-Goto...
If anyone reading this feels like trying something new, I encourage you to learn functional programming instead of finding creative-but-safe ways to use goto statements.
I think this opinion is a bit too coarse to describe current trends.
Certainly, current best practices recommend things like std::ranges, Streams, or LINQ instead of old-school for-i (or even for-each) loops, with good reason: the pipeline and map-reduce paradigms are very useful to simplify and shorten code especially when the same operation is applied throughout a data structure. This sort of operation also tends to be embarrassingly parallel, easily lending itself to performance boosts with SIMD and vectorisation.
However, pure, stateless functional programming is useful only in limited contexts, and implementations tend to rely on either a GC runtime, or resorting to copying and overwriting data structures to maintain correctness and safety at the (sometimes very large) expense of performance and efficiency.
Purely stateless and functional programming is extremely hard to use in overwhelmingly stateful (read: video games) applications without things slowing down to unusable levels. Update an ECS tag? Write the entire state (which can be megabytes large) all over again—no in-place updates, after all.
I feel most (non-functional) programming languages are adopting FP paradigms, but will maintain their imperative core by replacing old, leaky, and error-prone abstractions with data-flow paradigms (like the for-i -> map-reduce I mentioned above).
I think functional programming has it's advantages and it's worth learning. But no, imperative programming is still the most popular will remain so for some time.
IMO functional programming is just a special kind of imperative programming. I think real distinction is between "imperative/functional", where you describe how to do thing, and "declarative" where you don't care about the hows, but you specify what is the end result you want to see. For example: html (declarative) "I WANT A RECTANGLE AT THIS POSITION AND SIZE": <div style="top, left, height, width">, while imperative, you exactly tell the how, like <for i> then <for j> draw a pixel at that position i,j. Making drawing a box as functional does not make too much sense, because that is a task, which does not benefit from the functional paradigm, but it does helps a lot with cardinality and null checking problems when dealing with other kind of problems.
I think it is already changing into this. Basically every single framework is one kind of declarative wrapper over an imperative language, or you can say every single function library makes the way how you work a little bit more declarative (like in the box drawing demo, you can just use function to write a box. I think in 20-30 years, in a very twisted way programming will be completely declarative by you telling an AI to write a code, first at a very low level of abstraction, but in time more and more complex ways. We'll see
I wrote my parser by iterating character by character (foreach) and when reaching those states that need reprocessing I found it nicer to jump to a goto label at the start of the foreach, after the state variable was updated.
If anyone is aware of a purely functional HTML5 compliant parser let me know, as I'd love to steal some ideas.
[1] https://www.w3.org/TR/2011/WD-html5-20110113/tokenization.ht... ctrl-f reconsume
And as I've said, for HTML5 compliance you need to also parse JavaScript, see second sentence here https://dev.w3.org/html5/spec-LC/parsing.html#overview-of-th...
A good first smoke-test for HTML5 supporting parsers is the following html fragment
<input disabled>
XML-like parsers (masquerading as having "HTML5 support") will fail if tags aren't closed or self-closing, and if there are attributes without an explicit value.Well, no. Rational world use what makes sense for particular situation. Not silver bullet du jour that suddenly solves everyone's problem (not).
cough `longjmp()` cough. But yeah, it's not called goto...
I've never understood the hate goto gets, there are perfectly fine use cases for goto in most languages which have a goto statement. And even if one would expect otherwise most code I've seen using goto uses it in a sane way or at least intended way (that a modern language like Go can't break out of nested loops without using a label is just plain silly imho). In C there are a thousand ways you can shoot yourself in the foot, goto is nowhere near the top list.
The code was littered with gotos. Let’s say it was a 2000 line program with 100 gotos and no comments. It was very hard to follow the code flow. The “structured programming”/procedural style of naming each bit of (say) 25-50 lines of code according to its purpose and providing clear entrance and exit points was a huge advance in making code easier to work with by anyone other than its original author. Huge.
All variables were also global in those days, there were not locally scoped variables. This made gotos even worse. Reasoning about the state of a program combining global variables and lots of gotos could get quite complex for someone new working on an unfamiliar code base.
People don’t misuse goto nowdays because teachers, books, articles don’t teach it as a core primitive for flow control, due to shame. So now it’s kind of an advanced feature and not a problem. But back in the day, it made a real mess in the hands of a certain percentage of coders who invariably existed and lazily kept reusing existing lines of code, one spaghetti-mess of a goto at a time. If you’ve ever worked with someone else’s extremely/overly abstracted “DRY” code it was like that but 10x worse.
You mention shame and teaching and that's a valid point historically, I was told and taught the same things and wouldn't use goto at all until I figured out (myself) that certain kinds of cleanups and control flows become much less spaghetti and more readable by using goto. Instead of shaming and teaching a silly dogma "just because" people could have taught how to use it properly in those few idiomatic use cases. It's not rocket science. It's a shame (pun slightly intended) that some code is actually less readable than it could be just because someone was taught to never use goto because it was the easier explanation. It's like lying to children because you think they can't handle an honest explanation, in almost all cases it will bite you in the ass at some point.
They thought that if you banned things like loops, gotos multiple returns, etc you could produce a language suitable for 'real mathematics'. Turns out this is mostly garbage. But 50 years later it gets taught and people still believe it. People think recursion is this super duper thing when it's not. And people think multiple returns, goto, and continue statements are bad when they are fine.
IMHO, I believe the reasoning behind "exceptions are bad" comes from a similar place.
"Don't use goto it is bad"
"Don't use reflection it is slow"
"Don't do that it is an anti-pattern"
"Don't use X it is considered bad practice"
I understand there are rules of thumb when programming, but they are not set in stone. There is a reason the language designers haven't removed or deprecated features.
people don't just avoid but they actively oppose thinking for themselves. if it isn't a "principle" or a "rule" then it's wrong, and if there isn't a popular blog post granting you permission to do something, it's probably a bad idea.
you can take an idea, walk them though it, lay out the pros and cons, explain how it helps to solve a problem, even create a working prototype and show it to them, and the entire microsoft-brained team will watch it work then start groaning "hmmmmm, ehhhhhhhh, uhhhhhhh" in unison before deciding that they'd rather not solve the problem at all than do something "scary".
I do feel you regarding the culture (arguably, it’s a thing with many modern languages sadly) which is why I’m taking a year-long vacation to work on OSS projects - the language is beautiful but the atmosphere was insufferable in the last team. There are better places fortunately but one needs to recharge.
Incidentally, predicate transformer semantics are used to reason about imperative programs in an expressly functional mathematical way. They really are quite interesting. They’re also getting a bit more exposure thanks to modern theorem provers like Z3 and languages like Dafny.
I regretfully do not know my history will enough to know if he invented structured programming as we know it, but his advocacy certainly played a large role in its acceptance.
I've found it better to develop an intuition for these sorts of things, by making mistakes (or seeing those of others), and going from there. In the case of goto, as the author mentions, it really isn't implemented in modern languages the way it used to be (i.e. goto anywhere in the program). But sometimes it is necessary (e.g. early exit a try-catch block, jumping to an error-handling & cleanup block in gruesome circumstances, breaking out of deeply nested loops (never having deeply nested loops is another dogma I don't agree with), in state machines, etc.).
(No comments there, either.)
Uh, yes?
goto foo;
for (;;) {
if (0)
foo:;
}The goto being spoken about here is the unconstrained type that allows for both modification of jump location during run time and jumping in the whole address space of either program or system.
Also there are a few quirks here and there in a few languages, but it would break the flow to stop and enumerate the ones I know, plus I'd still miss some.
C is quite unruly, as it dates back to when this was a live debate, and this is one of the ways it being "portable assembly" is a reasonable statement. Duff's device is not possible in almost any other language. ISTR one other, but I'm not sure what it was... I may even be thinking about this: https://www.perlmonks.org/?node_id=388976 and that's 2004, not sure if perl can do that anymore.
I'm looking at the paper again. It clearly states that goto should be abolished from higher level languages.
Note that Dijkstra also lumps the repetition statement (while B repeat A / repeat A until B) with goto, and then carefully argues why that is an acceptable use of goto, namely that it is susceptible to analysis by induction.
I can't see how I can reach a conclusion other than that, yes, C has a goto of a type that Dijkstra considered harmful.
Some have argued that forward goto in C is easily analyzable, since it doesn't create loops. There are coding standards that permit forward goto, but not backward goto. What Dijkstra would have thought about the inclusion of a forward-restricted goto in a HLL is anyone's guess.
A C forward goto could jump from the middle of one loop into the middle of another one, though. Simply being forward isn't enough to ensure a certain property of the function's control flow graph. This is written about extensively in the Dragon Book.
* C-specific compiler-independent cleanup. This is completely unnecessary if your language supports proper destructors. Unfortunately many still-used and newly-invented languages are completely insufficient here. GNU C has `__attribute__((cleanup))` but it's kind of clunky.
* `continue` or `break` for an outer loop. One of the few things Java did correctly was supporting this directly, with names. A few languages support break/continue by number which is error-prone (fortunately they don't make the same mistake C did in adding `break`-skew for `switch, though unfortunately that usually means they also give up on efficient multi-way dispatch)
* nontrivial variants of `fallthrough` for a `switch`. This is generally an indicator that you should refactor out an `inline` function, but sometimes that makes the code significantly uglier since most languages don't support local-promoted-dynamic variables.
* computed `goto` in interpreter loops, state machines, etc. There is no viable replacement for this, though if your compiler is capable of inlining through an array of function pointers that helps (last I checked, GCC and Clang both failed very badly).
* coroutine implementations. Efficient coroutines have to be built-in to the language anyway (so they can optimize the existence of locals), but languages that support coroutines tend to abandon sanity all ye that enter here.
static int __init init_nfs_fs(void)
line 2046 without goto. See which version you prefer? I don't see goto use as mandatory.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...
Destructors throwing isn't broken, it just requires explicit care from the programmer to figure out which option to use in case of conflicting exceptions:
* If the existing unwind is a non-exception-unwind, there is no conflict. Exceptions thrown from a destructor just work™ and change it to an exception-unwind. Since C++11 you have to explicitly mark the dtor as `noexcept(false)` to indicate that you think you know what you are doing.
* If you're already winding due to an exception and want to preserve it, you can check for it at the start of the dtor, and wrap the dtor body in a try-catch which conditionally rethrows if there wasn't already one.
* If you're already unwinding due to an exception and want to ignore it in favor of the new exception ... doing this in C++ is pretty ugly but it's certainly possible. In order to use bare `throw` for the no-additional-exception case, you have to replace all stack variables with `option`-like wrappers so you can manually dispose them during `catch`. Other solutions exist if you don't care about correctly using bare `throw`.
* Or, you can do the safe thing and say "simultaneously unhandled exceptions means the programmer likely never considered how to handle this; give up"
Note that most languages that use `try-finally` have even bigger problems to deal with, since they have to worry about `return` as well as `throw`. Bugs and inconsistencies are frequent here.
[1] or trivially replacing with so many nested if statements.
Pretty much every language created in the past 50-some-odd years, encompassing effectively every language you will actually use, enforce bridled gotos, so for all practical purposes there is no bad use of goto.
But there's a reason even in languages that have it, you use it rarely. Structured programming is really good.