
Goto and the folly of dogma (2018) - luu
https://manybutfinite.com/post/goto-and-the-folly-of-dogma/
======
_bxg1
Over my first five years of professional programming, I've been thirstily
chasing the dragon of "perfect description". Early on I thought it was OOP.
Then entity/component. Then FP. Then it was _really_ about the type system.

Possibly the biggest lesson I've learned - both from the kiln of real-world
project requirements (within a multi-paradigm language) and from my
intentional ventures into other different and diverse programming languages
and frameworks - is that there's no such thing. There's no perfect way of
describing, not within a domain and certainly not across them. It's not just a
matter of abstracting farther and farther from real-world concerns,
sacrificing optimization until you're in descriptive nirvana. There are many
good ways to describe a given thing in code, and there are many more bad ways,
but there's no perfect way. Once I grasped that I became a much better (and
less stressed) programmer.

~~~
quickthrower2
Yes definitely. The essence is finding the right abstraction. The computer
doesn't care if you get this wrong, and the code could work perfectly, but it
can be a pain to maintain something if something is abstracted the wrong way.
And aiming to reduce the file size of your source files by "Don't Repeat
Yourself" isn't the necessarily always the best way to make code maintainable.
I've breathed a sigh of relief when I saw a code base that was your usual
scaffolded MVC app rather than something with a tonne of metaprogramming. I've
seen both, and the Keep It Simple principle has some merit.

Infact the best abstraction may depend on the team who will be maintaining
that code - so whether to use Tech A or B or Pattern X or Y might have, as an
important factor, whether you are moving office from one city to another, and
whether the job market is good or bad, affecting flow of people in or out of
the company etc.

------
hn_throwaway_99
I feel like engraving this paragraph in a wall:

 _Taboos tend to accrete over time. For example, overzealous object-oriented
design has produced a lot of lasagna code (too many layers) and a tendency
towards overly complex designs. Chasing semantic markup purity, we sometimes
resorted to hideous and even unreliable CSS hacks when much simpler solutions
were available in HTML. Now, with microservices, people sometimes break up a
trivial app into a hard-to-follow spiderweb of components. Again, these are
cases of people taking a valuable guideline for an end in itself. Always keep
a hard-nosed pragmatic aim at the real goals: simplicity, clarity,
generality._

From Java's "AbstractFactoryBuilderDelegator" insanity to "nanoservices", the
common thread to me seems to be overzealous decoupling, to the point where I
need to look in 10 different locations just to find out what happens during a
single request.

~~~
tluyben2
When you get more experienced most of these things make me laugh or cry
(depending on the siuation); it does not matter what companies like FB, Google
do; people on HN or Reddit will take it and do it to the extreme: we now ‘need
to’ use React for everything; if it does not fit, just beat it with a hammer
until it does. Kubernetes and microservices must be used for every tiny little
part if the app even if it causes a lot more overhead in performance/memory
use (computers are cheap and fast!) or debugging. Abstract almost everything!
(Java + OOP, Javascript and the npm mess) to Abstract almost nothing! (Go w/o
generics), Make everything reusable (left-pad), Rewrite everything in JS!,
Rust!, Go! etc etc. Everyone is running after eachother and doing it more
extreme and the end result is just as shit as if you would not have done that
at all and just thought about it bit before opening some IDE and codegenerate
you million lines of boilerplate with the instable and slow framework-du-jour.
As an older coder I sigh when a codebase is taken out of the ‘mothballs’ even
6-12 months after creation and people cannot get it running because everything
they used is outdated because the framework and library authors move fast and
break everything all the time. And ofcourse it is in an outdated language /
framework(Ruby on Rails is soooo pase) so noone knows anything , it uses the
358 most popular DSLs (350 unmaintained since january) at the time so unless
you drank the same coolaid it is a nightmare spelonking adventure.

At least Dijkstra had sound mathematical reasoning for his arguments and wrote
about them eloquently (and with good humor I may add); most of what is peddled
in the hipster coding circles is a smooth talk by a gifted social media
frontman that has no solid basis in anything besides that the person is
popular. I do not even understand how people dare to put their name on
complete messes like npm or one line npm packages unless it is a joke. I
assume things like leftpad are in fact a joke; if they are not I would have to
cry myself to sleep every night. So I just lie and say it is funny.

Only when someone codes something without any of that and it gets popular or
makes a lot of money, people come with ‘it was best for this occassion’. The
best example I can think off being anything Arther Whitney (k/kbd+) does; his
softare makes a ton of money, it is faster, smaller and, in my opinion, easier
to debug and uses less resources than most things I have ever seen passing
here (including what people call embedded; no people, something with a gig of
memory is not emdedded) and yet it pukes over almost all rules and styleguides
that everyone loves so much. Not to mention: he does something a lot of
programmers are jealous off (including me); he makes money with a programming
language and is always used here as a counter example when people shout that
programming languages that are not opensource and/or are commercial (even very
costly) do not work.

I wanted to write one sentence; it became slightly more, but I guess most of
it is on topic.

~~~
ritty
I'm probably going to take a lot of heat from all the young whippersnappers
out there for this, but I absolutely love your comment about React. I'm going
to save it. It totally describes my experiences with other developers. They
want to use React to re-write major portions of our codebase that work
perfectly well as is, just because React is super awesome! Can you guess how
many of our customers have complained that our website isn't a single page
application? I'll give you a hint, it's less than one. The devs will also make
little teeny projects that would take less than an hour to write in Vanilla JS
and make this big 20 hour development project that has a monolithic codebase
that all the sudden needs routers and back button integration and url mangling
and gigantic switch statements to draw the correct "page." Oh and don't forget
you have to set up all that webpack and and compiling routines so that you can
compile all that garbage into other garbage. And then you also have to do that
build over and over again for every change. This is JavaScript. Script is in
the name. It's not meant to be a compiled language. And contrary to our dev's
beliefs, React does not run or draw faster than Vanilla JS, unless you are
constantly redrawing the whole page in Vanilla JS, which no one does. I hate
React.

~~~
folkhack
In the United States the management layer doesn't have a clue so if you don't
keep up on React, GraphQL, etc etc - you're seen as a curmudgeon.

They're not the ones learning it but they're still attending all of the
conferences for it and with a non-practiced engineering capability they're
back to cargo cult BS.

Best to keep learning the new hotness or it's career suicide. Just remember,
for almost any 9-5 it's about the _narrative_ of work more than it is about
the work. Rewriting/changing huge portions of your already-working tech stack
is job security. I truly believe a huge portion of engineers engage in their
own "make-work" to justify their existence/paycheck.

~~~
tluyben2
Keeping up with something does not imply showhorning it into using it
everywhere but I agree with you.

------
ridiculous_fish
I was surprised by the number of gotos in the Python runtime. The link in the
article was down so here:

[https://github.com/python/cpython/search?q=goto](https://github.com/python/cpython/search?q=goto)

There's a lot of "goto exit" which is obviously a CPython runtime convention -
fair enough. However there's plenty of classically bad code, example:

[https://gist.github.com/ridiculousfish/ffe4fa2a17c831ed06e57...](https://gist.github.com/ridiculousfish/ffe4fa2a17c831ed06e57cfb2c467b25)

These are old-school-bad gotos: `if` statements would do the job more clearly.
Is this a broken-window phenomenon: one planted `goto` opens the door for the
rest? Or is there a deeper motivation for this style?

~~~
arcticbull
This feels less like 'folly of dogma' and more like these (C/C#) programming
languages don't have the constructs to safely and properly express what the
programmer is trying to do. 'goto exit' is an unsafe and dangerous version of
Rust's '?' operator.

> We should be willing to break generic rules when the circumstances call for
> it. Keep it simple.

I argue we should instead iterate on the programming language design to make
sure we don't need to make these kinds of trade-offs.

~~~
ridiculous_fish
C++ has solid "cleanup" constructs so I wonder why CPython is in C instead of
C++. Is it portability, compilation speed, complexity control, transition
cost, something else...

~~~
overgard
All of those, but also python came out in 1991 when C++ was still in its
infancy. Even if C++ had been mature though C is still a better choice, python
is often embedded in other programs and doing that with C has a lot fewer
headaches (simpler linking, better compiler support)

------
maxxxxx
Dogma is a real problem in this industry. When OO came up suddenly everything
had to be objects. So instead of writing

A=add(B,C)

You had to write

Adder AA; A=AA.Add(B,C)

I remember endless discussions about this and people always argued that
functions are not OO whereas I said OO is about state so no OO needed for
adding two numbers.

Same with goto. In FORTRAN it was an essential tool but suddenly it became
illegal and you had to write complex if statements and other things just to
get the same effect.

I guess software is so complex that it’s very to always understand all
drawbacks and advantages of something so you have to live by a set of rules
that usually work and follow them blindly.

~~~
seanmcdirmid
> Adder AA; A=AA.Add(B,C)

Did anyone actually ever do that or is this just a huge red herring?

Also, the above looks more like a data flow language where adders are
necessarily components in the wiring diagram (try building a CPU without
adders!).

Add can be a virtual method on B (so B.add(C)), but then you really want
Dylan-esque multidispatch on both B and C. But those kind of debates fell out
of style with the 90s.

~~~
worik
Borland C++ windows toolkit back in the early nineties would do that sort of
thing, if memory serves.

Really deeply convoluted OO.

~~~
pjmlp
And way better than MFC or ATL ever were.

------
idlewords
This rant kind of has it backwards, and Dijkstra's argument against GOTO has
been the victim of its own success. The use of GOTO statements he was
critiquing doesn't really exist in the wild anymore, so people see the tamed
version of GOTO we use to break out of nested loops and so on, and wonder what
the big deal was.

It's almost like an anti-vax argument. "This disease doesn't exist anymore,
why are we cargo-culting by vaccinating against it?"

The argument in the original rant was about the limits of our ability to
reason about code, and remains a deep and useful insight. The fact that we
don't really have examples of non-structured codebases to point to in 2019
shows how essential the invention of it was to our work.

~~~
kstenerud
GOTO is an easy target due to its cultural notoriety (regardless of how it
actually looked in the past), but the overarching argument is indeed against
dogma. To quote Donald Knuth:

"In the late 1960's we witnessed a "software crisis", which many people
thought was paradoxical because programming was supposed to be so easy. As a
result of the crisis, people are now beginning to renounce every feature of
programming that can be considered guilty by virtue of its association with
difficulties. Not only go to statements are being questioned; we also hear
complaints about floating-point calculations, global variables, semaphores,
pointer variables, and even assignment statements. Soon we might be restricted
to only a dozen or so programs that are sufficiently simple to be allowable;
then we will be almost certain that these programs cannot lead us into any
trouble, but of course we won't be able to solve many problems."

It's a problem as old as time itself: A smart person makes an observation
based on deep understanding, and the rest, rather than go through the
cognitive load of learning its fundamental roots, convert it to an easy
statement of morality and dogma, shrouding it deeper and deeper with ceremony
and pomp to create a mystique that none dare investigate.

Thinking is hard, and takes much energy. Most people prefer to keep that to a
minimum, thus our superstitions, dogmas, cults, and priesthoods.

~~~
jerf
"GOTO is an easy target due to its cultural notoriety (regardless of how it
actually looked in the past), but the overarching argument is indeed against
dogma."

I agree.

But I think it's worth pointing out that if we're going to use reluctance to
use goto as an example of dogma, it strengthens the anti-dogma argument _even
more_ to point out that the dogma isn't even correct _on its own terms_ ; the
goto that the dogma is rejecting historically isn't the same goto that exists
today.

Under many dogmas lies a kernel of truth. That kernel can be worth extracting,
and is often quite enlightening, unlike the dogma.

------
ncmncm
It has been decades since I was tempted to "goto". This not because of dogma
or "drinking the kool-aid". It is because I use an expressive language that
has constructs that mean what I mean, so don't need to be cobbled up out of
such fragmentary primitives.

That so much C code is littered with them just demonstrates a deep weakness in
C, and not any kind of fundamental principle. I admit surprise that C# turns
out similarly weak.

~~~
asveikau
Would you also consider the assembly code that is generated by your high level
language to be so "littered" with jmp instructions, arising from a "deep
weakness"?

It's one thing to prefer to work with another abstraction, but this is awfully
judgmental phrasing that denies or unfairly maligns a usefulness and
_necessary_ ubiquity at a different level.

~~~
AnimalMuppet
Yes, assembly language is a language of deep weakness. There's a _reason_ we
don't use it unless we have to - it's too hard to write anything in assembler.
In fact, assembler weaker than C - in C, you can usually avoid goto if you
want to bad enough, but in assembly, it's impossible.

~~~
asveikau
> it's too hard to write anything in assembler.

And yet, everything you run is written in it. (By a compiler or a JIT, sure.)
The goto is a useful abstraction _for its layer_. It doesn't have to be your
favorite layer, but it's there, and ubiquitous.

I feel like discussions around memory safety are similar. I can't get a lot of
people around here to admit that in order to be blessed with memory safety at
one layer it needs to not exist somewhere else, and that's OK.

~~~
AnimalMuppet
You seem to be having a different discussion than most of the rest of us.
You're claiming that it's fine for its layer, and the rest of us are saying
that _we don 't want to work at that layer_.

Yes, jmp is useful at the assembly layer. Yes, everything eventually gets run
on assembly (on the way to microcode, and then transistors, and then quantum
mechanics). That doesn't mean most of us want to work there, though.

And goto is the same. Having seen that we don't have to work in that way, we
don't _want_ to work in that way. We can work with larger abstractions so that
we don't have to deal with that kind of detail.

~~~
asveikau
> You're claiming that it's fine for its layer, and the rest of us are saying
> that _we don 't want to work at that layer._

Correct. This is what I said all along. Glad to see you're up to speed.

Meanwhile, every time you write an if statement ... May you think,
acknowledge, appreciate: "I'm adding a goto!" Or possibly several of them. [I
am pretty sure I have had discussions with people who say they are also
against if statements, but I don't think that's quite as common.]

~~~
ncmncm
Everyone is always perfectly and completely aware of the jmp instructions that
implement their if and while statements. Talking about them does not make you
cleverer than anyone else.

What you are missing, and is the fundamental essence of the whole discussion,
is that these jmp instructions don't just jump to any old place, like a goto.
They jump to only very specific places corresponding to the boundaries of our
if and while statements. The compiler will never generate an undisciplined
branch, absent an actual goto in the source.

Beneath the jmp instructions there are register transfer machines, and beneath
them are logic gates, and beneath them are transistors and wires, and beneath
them are charge carriers and fields, and beneath those are atoms and
crystalline structure.

At each level you can find the correspondence with structures in the next
level above and below. In no case does the lower level violate the structural
rules of the next level up, despite that in principle, it could. That is how
we get systems that can be understood, and work.

~~~
asveikau
> Talking about them does not make you cleverer than anyone else.

Please don't assume that any notion of my own cleverness is the crux of what I
am saying or has anything to do with it.

> At each level you can find the correspondence with structures in the next
> level above and below. In no case does the lower level violate the
> structural rules of the next level up, despite that in principle, it could

Disagree, especially since you went so far as to talk about the physics. There
is a lot of order created from chaos, and the structural rules are largely
fiction, taking some effort to impose them.

~~~
ncmncm
But they are, in fact, imposed, or you would not be able to read this; thus,
fictional only in that they were invented.

But in any case, and to the point, there is nothing fundamental about jmp
instructions. They, and the sequential execution they interrupt, are a way to
help organize state machines. It is a triumph of decades of effort that we
have succeeded in making state machines of such complexity behave in
comprehensible ways, and a deep failure that we have not found any better way.

------
aikah
It's funny how Go limitations made me go back to using GOTO statement to deal
with errors in an http handler.

------
noelwelsh
There is some nuance here that the author misses. Goto jumps to a location in
program text. Other techniques, like (single shot) continuations, jump to
program state. The former is dangerous. Not just because you can write
spaghetti code, which was the original critique against goto, but because you
can make jumps that have no meaning. For example, you can jump to a location
that has not been initialised yet. With continuations you can still write
complicated control flow, but you can only make jumps that are meaningful.

So I argue the issue is not with goto per se, it is with the lack of better
tools provided by the languages in question to express complicated control
flow. Like many things in programming languages, better tools are well studied
but not available in most mainstream languages, which are stuck in ~1980s
paradigm.

------
pjmlp
> When Linus Torvalds started the Linux kernel in 1991, the dogma was that
> "monolithic" kernels were obsolete and that microkernels, a message-passing
> alternative analogous to microservices, were the only way to build a new OS.
> GNU had been working on microkernel designs since 1986. Torvalds, a
> pragmatist if there was ever one, tossed out this orthodoxy to build Linux
> using the much simpler monolithic design. Seems to have worked out.

Except that desktop is the only place standing where microkernel haven't fully
catched up, and even then macOS and Windows have a kind of compromise between
monolithic and microkernels, with plenty of stuff running on userspace,
increasing with each release.

Even Project Treble pushes several drivers into userspace processes, with
Android IPC to talk with the kernel layer.

Had Hurd gotten the same love from IBM, Compaq, Oracle, Intel,.... as Linux
did, and it might have turned out quite differently.

------
8077628
Having saved myself a headache earlier by parsing some HTML with regex, I'm
appreciating this post. On the other hand, if you don't obey dogma, it may
impair the delivery of your cargo. Everything is a tradeoff.

~~~
wahern
There's a difference between admitting that corners need to be cut sometimes
and arguing that cut corners are _correct_.

You didn't "parse HTML" with a regex; you created a solution to fix a very
narrowly circumscribed problem by pattern matching on some string inputs. Big
difference. Were an easy to use HTML parser (or likely lexer) readily
available there'd be little excuse to cut corners as the proper solution would
likely be far easier to prove correct (formally or informally) than the regex
hack. (Full disclosure: I've written an HTML5-compliant streaming HTML lexer
precisely so I--and others--would have less reason to depend on regex hacks in
security scanners.)

The article says that the Linux approach proved good enough. No, it didn't.
Linux has turned into a nightmare of security vulnerabilities, on par with
Windows 95, just as originally prophesied. We only tell ourselves it's good
enough because we're unwilling to admit we're where at. Remember when Linux
and open source were paragons of security? Man, how times have changed....

But now we have a formally verified operating system in seL4, which is...
[wait for it...] a microkernel. Of course, it's difficult to use as a general
purpose OS, though not far from where Linux was in the 1990s. In time we'll
get there. In the meantime no good comes from lying to ourselves about the
nature of our solutions.

~~~
majkinetor
> Linux has turned into a nightmare of security vulnerabilities, on par with
> Windows 95, just as originally prophesied.

What exactly are you talking about ? What was 'originally prophesied' ?

~~~
wahern
That monolithic kernels are more susceptible to attack because they're less
resilient to programming errors. This was one of the arguments in the famous
Linux v MINIX debate(s), but the notion that microkernels were more secure
goes back to before the term microkernel was even coined (i.e. before 1980s).

------
cortesoft
Isn't "keep it simple" also dogma?

~~~
cjfd
It can be. Sometimes the requirements are complex so any expression of them in
code would also be complex. Then another person sees this complex code and
automatically assumes that it is bad and should be made less complex while
completely ignoring the fact that it would break the requirements. And to the
person who is now going to interject that the requirements should be simpler.
I am all for that if possible, but in many cases it is not. E.g., if they are
written in contracts. Of course, bad programmers will create complexity where
none is needed.

