In this letter, Dijkstra talks about readability and maintainability in a time where those topics were rarely talked about (1968). This letter was one of the main causes why modern programmers don’t have to trouble themselves with goto statements. Older languages like Java and C# still have a (discouraged) goto statement, because they (mindlessly) copied it from C, which (mindlessly) copied it from Assembly, but more modern languages like Swift and Kotlin don’t even have a goto statement anymore.
TIL that C# and Java have a goto statement.
Forget about goto, the latest version of C# introduced what’s essentially comefrom.
To be fair, await
is a bit more like comefrom
, and it’s been around for a few releases now.
Following that logic if
, else
and while
are also “pseudo goto” statements.
There’s nothing wrong with conditional jumps - we couldn’t program without them. The problem with goto specifically is that you can goto “anywhere”.
In C# at least, goto
can take you between case labels in a switch statement (rather than using fallthrough), which I don’t view as being nearly as bad. For example, you can do goto case 1
or goto default
to jump to another case.
The only other use of goto
I find remotely tolerable is when paired with a labelled loop statement (like putting a label right before a for
loop), but honestly Rust handles that far better with labelled loops (and labelled block expressions).
I’ve programmed C# for nearly 15 years, and have used goto
twice . Once to simplify an early break from a nested loop, essentially a nested continue
. The second was to refactor a giant switch statement in a parser, essentially removing convoluted while
loops, and just did a goto
the start.
It’s one of those things that almost should never be used, but the times it’s been needed, it removed a lot of silliness.
For C it makes sense. The point of C is that it can work as a low level language. Basically, everything doable with assembly SHOULD be doable with C, and that’s why we don’t need another low level language that’s basically C with goto.
Even though almost all of C users should never use goto.
C is one of the few languages where using goto
makes sense as a poor man’s local error/cleanup handler.
Yeah. Without a proper error handling mechanism, goto is actually useful for once.
switch statements are three gotos in a trenchcoat.
In most CPU instruction sets, the only conditional instruction is branch (aka goto).
For such an influential letter, I don’t find his arguement all that compelling. I agree that not using go to
will often lead to better structured (and more maintainable) programs, but I don’t find his metric of “indexable process progress” to satisfyingly explain why that is.
Perhaps it’s because at that time people would be running the programs in their heads before submitting them for processing, so they tended to use more of a computer scientist mindset - whereas now we’re more likely to use test cases to convince ourselves that code is correct.
I think it’s convoluted way to describe it. Very technically-practical. I agree it’s probably because of historical context.
The argument I read out of it is that using goto
breaks you being able to read and follow the code logic/run-logic. Which I agree with.
Functions are similar jumps, but with the inclusion of a call stack, you can traverse and follow them.
I think we could add a goto stack / include goto jumps in the call stack though? It’s not named though, so the stack is an index you only understand when you look at the code lines and match goto targets.
I disagree with unit tests replacing readability. Being able to read and follow code is central to maintainability, to readability and debug-ability. Those are still central to development and maintenance even if you make use of unit tests.
I wasn’t saying that unit tests replaces readability, I was saying that back in the 60s they’d reason and debug using their brains (and maybe pen and paper), with more use of things like formal proofs for correctness. Now that we write more complicated programs in more powerful environments, it’s rare to do this (we’d use breakpoints, unit tests, fuzzing, etc).
Perhaps it’s because at that time people would be running the programs in their heads before submitting them for processing, so they tended to use more of a computer scientist mindset - whereas now we’re more likely to use test cases to convince ourselves that code is correct.
This is 1968. You didn’t have an IDE or debugger. Your editor was likely pretty terrible too (if you had one). You may have still been using punch cards. It’s possible the only output you got from your program was printed on green-bar paper.
“Back in the day” it wasn’t uncommon to sit with a printout of your code and manually walk though it keeping state with a pencil. Being able to keep track of things in your head was very important.
GOTO existed in part for performance purposes. When your CPU clock is measured in megahertz, your RAM is measured in kilobytes and your compilers don’t do function in-lining it’s quicker and cheaper to just move the program pointer than it is to push a bunch of variables on a stack and jump to another location, then pop things off the stack when you’re done (especially if you’re calling your function inside a loop). Even when I was programming back in the '80s there was a sense that “function calls can be expensive”. It wasn’t uncommon then to manually un-roll loops or in-line code. Compilers are much more sophisticated today and CPUs are so much faster that 99% of the time you don’t need to think about now.
Oddly enough the arguments against GOTO are less important today as we have much better development tools available to us. Though I certainly won’t recommend it over better flow-control structures.
When your CPU clock is measured in megahertz, your RAM is measured in kilobytes
Ah yes, the good ol’ days when developers programmed for efficiency.
Mostly because they had to. Writing efficient code and easy-to-read code are often at odds with each other. I like being able to create lots of functions that can be called from a loop without needing to worry too much about function call overhead. I can prioritize readability for some time before I ever need to worry about performance.
Languages don’t have goto because they mindlessly copied it.
It’s not idiotic. You can do a lot of performance optimizations with GOTO so providing it as a “use it if you know what you’re doing” option is fine. And some things are easier to read with GOTO.
FWIW the Linux source code is full of GOTO statements. Nearly 200,000 of them in fact.
There’s a solid reason for goto in C.
Bringing goto into Java was (and is) idiotic.
If you’re trying to squeeze every ounce of performance out of your code then you’ll need those optimizations.
But any higher level language than C the entire point is to write easily maintainable and useful code that any idiot can go, read and update. A goto is antithetical to readability.