10 ms·
In this case, the questionable choice of a "something" to handle the complexity is jarringly at odds with the high economic importance that the comments convey.
by arduanika 8y ago
In this case, the questionable choice of a "something" to handle the complexity is jarringly at odds with the high economic importance that the comments convey.
> tests
pv_controller.go has 1715 lines. To be generous, we might say half of it is comments. pv_controller_test.go has 359. Hopefully this code is exercised elsewhere in integration tests?
> a huge QA department
That's what you're signing up for when you choose a language that expresses cases mainly using if/then/else, and when you don't feel like testing every case.
> into the type system
Sum types aren't complex, although they're not familiar to everyone in the way i/t/e is. Even a result type (a straightforward example of a sum type, available e.g. in Rust) would simplify a lot of the "if err != nil" boilerplate, and greatly de-indent this code. It would probably also make invalid cases unrepresentable in a few parts of this file, eliminating a few more branches. In fact, a sum type typically requires an exhaustive pattern match, making practices such as their rule "every 'if' statement has a matching 'else'" the default.
My point is, complexity (and economic utility) are not conserved as you choose between these "somewhere"s. A few basic type system features can drastically reduce complexity for the QA team or eliminate comments and cases from the space shuttle.
Still - good on them for this degree of discipline & for so many well-written comments, with clear contracts about what is modified or not. ATBGE.
- bitexploder 8y agoI don’t think this changes the inherent complexity of the code right? The basic logic of what is being done is still the same. De-indenting code but still having the same complexity, some of which is now abstracted by the language is still complexity. Maybe it somewhat helps the humans reading the code? But don’t you still have to reason about the basic state changes of the system the same way?
- dnautics 8y agothe code itself has the same complexity, but you are pushing the workload to the compiler, not to the human. Given the two options I typically prefer the compiler.
- AstralStorm 8y agoCompilers and libraries are written by humans and are probably the most fallible things ever, optimization wise. I'm not talking 1% here. I'd love to see an opposing compiler with an automated proof that it does what it claims. Well, unless your computer can read and parse high order proofs over code. Not even Haskell can do that. (It can barely parse F types much less optimize them.) The closest I have come to that feature is Isabelle's simplify and even that is limited. GCC and Clang can do basic bounds proofs at best and otherwise are tough buggy heuristic beasts. Just look at the trackers. GHC has a slightly easier job but it's paid for in programming complexity. There are hoops in type system you have to jump to get performant code... and even then the code generated its at best meh quality. There is always some joker telling that it could be compiled for SIMD or multithreaded but that never materialized in usable form.
- Touche 8y agoCompilers can only tell if programs are internally consistent, they can't help ensure that they are correct. Compilers don't know about inputs. Compilers don't know if the branch you wrote goes in the correct direction for a given input.
- mywittyname 8y agoWell, the compiler can optimize away any superfluous branches which exist entirely to act as hints for the programmer.
- arduanika 8y ago> they can't help ensure that they are correct Of course they can help. Maybe you meant: > they can't ensure that they are correct
- woah 8y agoIt prevents dumb mistakes like not covering all cases
- eecc 8y agoMonadic Try, biased Either, Some, pattern matching and destructuting... you don’t even need to know category theory to use and understand them in code, and you let the compiler do all the tough work. This example - despise the perplexing celebrations - is a product of the limits of Go
- danieldisu 8y agoYet you can argue that despite it's limitations they could make working (complex) software with it.
- masklinn 8y agoThat's not an argument, that's a truism. You can make working (complex) software using nothing but machine code keyed into volatile memory via front-panel switches.
- martinlaz 8y agocan you really?
- lmm 8y agoIsn't that a case of "shit crayons"? People can and do make great things with terrible tools, but that doesn't make those tools any less terrible.
- dragonwriter 8y ago> This example - despise the perplexing celebrations - is a product of the limits of Go All coding style is a product of the limitations of the language (well, except when it is coding style dictated by the limitations of a different language applied in a cargo cult fashion out of it's appropriate context); and celebrating the choice of how to approach this problem given that it was being done in this language isn't the same as celebrating the language choice. Go would be pretty close to my last choice for anything, but that doesn't stop me from recognizing this as a thoughtful way to apply Go to this problem.
- rakoo 8y agoSo basically complexity would go in the language
- lmm 8y agoA language that spends its complexity budget well can save complexity from a lot of programs. E.g. if you look at https://philipnilsson.github.io/Badness10k/escaping-hell-with-monads/ https://philipnilsson.github.io/Badness10k/escaping-hell-wit... , language A could offer all 5 of those ad-hoc solutions (making it a very complex language) and language B could just offer monads (making it a relatively simple language), but both languages would be just as effective in alleviating the complexity of programs written in that language.
- AstralStorm 8y agoAnd then language C(++) just skips unnecessary monads which are syntactic salt and uses exceptions, while providing the optional type for where you really, really want this behavior. Tradeoffs of course, but these were considered. The main problem here with this "solution" is that any monad looks like every other monad. You can easily lose context and have to rely on naming conventions, which is quite terrible. You cannot spot what the code is doing at a glance. And if you mess up, the type system and compiler will spout some completely unhelpful message because it parses everything the same. Code that does very different things should look different, not identical. (Unlike what Lisp and FP people think.) Just think on why we do not use textual buttons everywhere in UI. It's a major reason fewer people accept Lisp than could...
- lmm 8y ago> And then language C(++) just skips unnecessary monads which are syntactic salt and uses exceptions Exceptions are an ad-hoc solution to a sixth problem. You still have the other five. (Ok, it's possible to use exceptions to replace null. But that still leaves the other four). C does not have exceptions, or any solution to error-checking hell at all. C++ is a notoriously non-simple language. Neither is at all convincing as a counterargument. > Code that does very different things should look different, not identical. In a language with monads, code that does things that are specific to error-handling still looks very different from code that does things that are specific to async I/O. But code that does a thing that is fundamentally the same (parametric), such as composition, looks the same. This is the very essence of programming (and indeed of mathematics): "2 + 2" does something that is, on the surface, very different from "3 + 5", yet there is an important underlying commonality. Code that sorts a list of integers is doing something that is, on the surface, very different from sorting a list of strings, but there's an important underlying commonality. Monads just take the same thing one level higher.
- lima 8y ago> pv_controller.go has 1715 lines. To be generous, we might say half of it is comments. pv_controller_test.go has 359. Hopefully this code is exercised elsewhere in integration tests? The PV subsystem interfaces with external storage systems, so integration/end-to-end tests are much more useful than unit tests (yes, they do exist).