8 ms·
Goto and the folly of dogma (2018)
- _bxg1 7y agoOver 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 7y agoYes 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.
- idlewords 7y agoThis 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.
- wahern 7y agoThis comment, https://news.ycombinator.com/item?id=19962895 https://news.ycombinator.com/item?id=19962895, from a few days ago explains that any use of goto broke Dijkstra's theoretical formalization of program structure. Accordingly, even today's minimal use of goto would still be harmful as it would break his formalization model. Fortunately we have better models that can handle goto. So it's not really like the anti-vax movement because the most substantial thing that changed isn't our use of goto (the disease burden in your analogy) but better formalizations (i.e. we have better medicine that makes fewer demands of the patient).
- jerf 7y agoNo, it explains that any use of the original goto operator would break the formalization model. No current language has the goto model that Dijkstra was advocating against; he won. To put it in modern terms, the goto in question would be exposing to the programmer the raw "jmp" assembly instruction. No high level language does that. Indeed the best way to understand what they can and can not do is precisely to understand that formalization model and understand when you can't do a certain thing because it breaks it.
- AnimalMuppet 7y agoUm... can't C jump pretty much anywhere (at least within the same function)? Including insane things like into loops? Is that enough to break Dijkstra's model? (Or are you excluding C from "high level languages"?)
- jerf 7y agoWell, I'd certainly exclude C from modern high level language, but I am obviously forced to concede that's not what I said before. :) Instead, I'll have to confess ignorance; I didn't realize that C's goto was that insane but it seems it is. https://stackoverflow.com/questions/6021942/c-c-goto-into-the-for-loop https://stackoverflow.com/questions/6021942/c-c-goto-into-th... I'll have to ask the reader to suitably modify the post above. The tamed goto made available in most modern high level languages can't break the analysis, because the compiler will reject it, i.e. https://play.golang.org/p/2tA7Bpof611 https://play.golang.org/p/2tA7Bpof611 (hit "run" to (try to) compile)
- 8077628 7y agoHaving 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 7y agoThere'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.
- cefstat 7y ago> Remember when Linux and open source were paragons of security? Man, how times have changed.... I remember a time when Linux was a paragon of security compared to the corresponding Windows version, Windows 95. I do not remember a time when Linux had no vulnerabilities. What happened is not that Linux got worse but that Windows got much better.
- majkinetor 7y ago> 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' ?
- hn_throwaway_99 7y agoI 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 7y agoWhen 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.
- deleted 7y ago[deleted]
- ridiculous_fish 7y agoI 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/ffe4fa2a17c831ed06e57cfb2c467b25 https://gist.github.com/ridiculousfish/ffe4fa2a17c831ed06e57... 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 7y agoThis 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 7y agoC++ 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 7y agoAll 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)
- vkazanov 7y agoCPython is a relatively simple program calling for a (relatively) simple language. It's meant to be understood by lots of people, used within many different contexts and C is in much better position here. At its heart it is just a bytecode interpreter, i.e. a loop with a huge switch. I don't think you need an elephant language just for that.
- maxxxxx 7y agoDogma 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.
- deanCommie 7y agoCargo culting OO helps no one, but neither does dismissing it out of hand. Here are some other things that Adder AA; A=AA.Add(B,C) has over A=add(B,C) that you are glossing over 1) You move the adding logic to it's own file, with other adding-only responsibilities 2) You allow injecting different implementations of Adder - maybe introducing a more efficient one in some cases but not others 3) You enable mocking Adder so that your logic that verifies that B and C are added can be tested without having to re-add B and C all the time. Not sure how much of a concern this was at the time when OO paradigms were first being created, but in today's world where .Add might be a call to different cloud services, and where unit tests with good mocking are essential for any serious application, these concepts are essential.
- clarry 7y ago> 1) You move the adding logic to it's own file, with other adding-only responsibilities The code snippet above says nothing about where the adding logic is, other than that one places it in a method while another uses a straight function. Said method/function can live anywhere, unless you have a specific language in mind that prevents you from moving functions to a file? > 2) You allow injecting different implementations of Adder - maybe introducing a more efficient one in some cases but not others The thing the code wants to do is add something, so if that can be made more efficient, it can be made more efficient by implementing a more efficient add function. Why is it that you need to introduce something that is not an add function, in order to inject a more efficient add function? Why can you inject an Adder but not add()? What language is that again? > 3) You enable mocking Adder so that your logic that verifies that B and C are added can be tested without having to re-add B and C all the time. This makes no sense to me. If you add B and C, you test it once, so what's the deal with this re-adding? And why can't you mock add()?
- cortesoft 7y agoIsn't "keep it simple" also dogma?
- cjfd 7y agoIt 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.
- ncmncm 7y agoIt 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 7y agoWould 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.
- ncmncm 7y agoMachine instructions are the archetype of fragmentary primitives. We use better languages for good reasons.
- AnimalMuppet 7y agoYes, 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 7y ago> 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.
- noelwelsh 7y agoThere 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 7y ago> 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.
- aikah 7y agoIt's funny how Go limitations made me go back to using GOTO statement to deal with errors in an http handler.
- Ngunyan 7y agoBlind acceptance of Edsger Dijkstra's writings considered harmful, such as goto and array index starting at 0.