12 ms·
Code boilerplate: Is it always bad?
- platz 9y agoI bet Fortran has an even lower 'proportional' variableness.
- xapata 9y agoMeaningful names are critical for understanding. Without good names, it's incomprehensible, regardless of the complexity.
- Ace17 9y agoExactly. If you abstract things but don't give meaningful enough names, you loose everything (because your callers are still forced to lookup the definitions of your abstractions, and now this definition can be hard to search for).
- SomewhatLikely 9y ago"By the time I’m done—adding more recovery scenarios, better logging, augmenting errors with more context, handle more edge cases—most of my boilerplate gets a lot more interesting. I did not realize that good boilerplate could be fertile ground for iteration." This point makes sense, however I think I like to have my cake and eat it too. Java 8's stream constructions can be relatively terse, but if you need to switch back to a for loop, IntelliJ has nifty right-click menu items for doing so. I think the argument made by TFA is at least related to John Carmack's take on inlining vs. abstraction: http://number-none.com/blow/john_carmack_on_inlined_code.html http://number-none.com/blow/john_carmack_on_inlined_code.htm...
- buzzybee 9y agoI followed Carmack's suggestion and never looked back. I write simpler code on average now, and I spend less time having to re-read it too because I'm changing the same lines less often. What I lose in terseness in the short-term, I regain in being able to spot key abstractions that are more crucial to long-term program health. It plays well with debugging since there are fewer places where state can act at a distance. It's very unfashionable advice in some circles, though. It sounds just enough like "go back to writing unstructured copy-paste spaghetti" to trigger some people's kneejerk. But the ground rules of "don't take subroutines, don't jump backwards" actually keep it from getting too far out of control, because they align you towards code that only uses enough flexibility for the task at hand, hence there's a lower likelihood of missing something. Very good practice for greenfield code where you might reflexively add a generalization that you don't use. I can still get better at it. Edit: And one of the most important things it taught me, is that I can use scope blocks and a comment instead of a function, and it'll be sufficient.
- macscam 9y agoI assumed the title was about boilerplate in terms of some skeleton or framework you download. Turns out this is different from what youre talking about (verbosity). Its interesting that even in programming jargon we have two uses for the same term. They are not totally separate though. The repetitive, DRY violations eventually get abstracted into a framework, at which point the boilerplate becomes ok. And if its still too verbose, than another boiler may arose to encompass it.
- mk-61 9y agoWell, when you have several projects using same framework/skeleton, and the latter is downloaded and copied over and over again in every project - it became verbose on highest level and can be called boilerplate.
- braveo 9y agoThe author doesn't understand what boilerplate means.
- w-ll 9y ago>> Any time we write similar-looking code over and over, that’s considered copy-pasta. is what i'd call it.
- slaymaker1907 9y agoYes. By using higher level constructs, it is much easier to see what is going on in the code. If there are too many lines, it is much more difficult to understand a piece of code as a whole.
- braveo 9y ago> Any time we write similar-looking code over and over, that’s considered boilerplate. That's not what boilerplate code is, and DRY is not the way you get rid of boilerplate (the article claims it is a part of the solution). boilerplate is a repeated preamble that's necessary and unchanging in order to get down to the business of actual logic. An easy example of boilerplate would be include guards in a C/C++ header file, or 'use strict' in perl. For example, the author says this: > When I write Go, I noticed that my tweaks almost always go exactly where they naturally belong. My boilerplate doesn’t stay untouched! If you're "touching your boilerplate" while writing code that affects the logic of your system, then it isn't boilerplate.
- makecheck 9y agoBoilerplate can be common and still be tedious. Those examples are just clear indications that languages sometimes have really terrible default behaviors. Namely, Perl should have been strict by default (with a "no strict" if this didn’t perform well enough), and C++ should have at least added #import.
- ovao 9y agoInteresting take. I think everyone can find something in this with which to agree, so I don't think it's a particularly polarizing way of thinking, but I haven't seen terminology like this used to describe it before. I fall into the same psychological trap in C++ that the author falls into with Python: the "if this is over here instead it's more [elegant/right/concise/impressive/less boilerplatey]". Unfortunately, I find it too tempting to value beauty and elegance (and often masking real weight and complexity) over the in-your-face 'rawness' of simple loops, branches and so on. In Go this is harder to do in general, so you only try for so long before you learn not to fight it. This comment really spoke to me: > Maybe this is subjective but I posit that the code’s shape looks more like what it is. Indentation isn't merely stylistic or syntactic, but it tells us useful things about code at these lower levels. A more complex code shape should convey more complex logic, and we should want that complexity apparent to us...at least sometimes. It looks great to have a nice, terse one-liner, but visually flattening possible control flow paths feels really nasty.
- atilaneves 9y agoI didn't agree with anything. The Python version is leagues better.
- meric 9y agoI iterate code like he does, then when it gets to where it's doing what I want it to do, I refactor it to make it more readable even if it reduces iterability. After the initial phase of writing the code, going back and forth, etc, the code is going to be read many more times than edited, if in the future it needs to be edited again, the future editor can refactor the nice looking code into whatever shape, and the important thing was that future editor understood my code in the first place, and I hope that future editor, when done editing, will refactor the code into being easily read for the next person, too.
- tigershark 9y agoIt's always bad. The second line makes you understand with a glance what it does because it is declarative and explains what is doing instead of how. The go code is awful (personal opinion, although I hope it is widely accepted), much more verbose and it opens you up to a whole series of errors that are just impossible in the python version. Boilerplate code by definition is just duplication and it is always bad because opens you up to bugs in a specific part of the boilerplate while you can't have any bug at all in any boilerplate if you don't have any of it. Honestly I can't really see how anyone can prefer code that is more verbose and bug-prone to code that is more concise, clear and with less bugs.
- banku_brougham 9y agoYour comments clearly ring true, for the example code snippets shown. But i think you have missed the point of the article, which is the benefit of the Go version when iteratively changing the structure. The author could have brought the examples forward, instead of just providing links to urllib3 and the Set type.
- coldtea 9y ago>Your comments clearly ring true, for the example code snippets shown. But i think you have missed the point of the article, which is the benefit of the Go version when iteratively changing the structure. You can iteratively change a smaller, terser code, must faster. There's a reason prototypes are often written in languages like Python.
- themodelplumber 9y agoThis argument can be abstracted to subjective thinking vs. objective. In subjective thinking, every situation is unique and even the best objectively-validated boilerplate can increase risk by lowering flexibility, control, and responsibility for & deep knowledge of the situation. Objective thinkers tend to go looking for existing, established boilerplate or frameworks because of the immediate leverage that is provided. They may think of too much subjective thought or originality as ridiculous NIH or wheel-reinvention. Their struggle is in the weighty task of grokking and maintaining third-party resources. Scott Adams has kind of unwittingly uncovered this dichotomy with his own inventive, subjective thinking. He considers more objective thinkers as "word thinkers" or some name like that, because they go looking up the established names of things rather than understanding the things themselves. Also Chris Langan similarly trashed Jeopardy contestants in a news interview, because they aren't subjective thinkers like he is; their minds are just filled with objective facts about various things. Well, true for him; his bias dictated the way he developed his own unique universal model--he prefers and has a gift for subjective thought. Really though, we need both kinds of approaches.
- raz32dust 9y agoTo take an analogy, it is similar to saying that we should use small words when trying to communicate. But once you understand the meaning of the more complex words, it is more fluent to communicate in terms of the longer words which represent the abstract concept more concisely and correctly. Of course it actually holds true to some extent in case of spoken languages, but that is because your intended audience is not expected to understand all the words in the dictionary. But the programmer who is reading the code is expected to understand the whole dictionary of python, including all the keywords, constructs and idioms. It gave me some thought, but ultimately I think don't think it is a correct argument.
- oblio 9y agoTo take your analogy to its conclusion, we don't want to communicate only using small words, true, but we don't want to communicate only using big words, either. Ain't nobody (= most people) got time for Hegel :) I suspect that's why some of the very "expressive", i.e. dense languages, aren't widely adopted.
- deleted 9y ago[deleted]
- janwillemb 9y agoWhen I see the error-handling examples, I think that the problem with this kind of "boilerplate" is that it's easily forgotten. This results in errors being eaten and that's bad. But I agree with the main point that "proportionality" of code is a good thing.
- collyw 9y agoSo expressiveness of a language is now seen as a bad thing?
- rev_null 9y agoAmong golang developers, yes.
- deleted 9y ago[deleted]
- olavk 9y agoIt seems he have been writing code so clever and compact that he didn't dare to change it when it came to refactoring time: > I’d take any opportunity to write terse code. It feels good to be clever > I noticed that when I wrote Python code, I would often go out of my way to avoid messing up a beautiful terse set of lines by moving logic to places other than where it might naturally belong. I can see how a less expressive language might help with this problem, but I think a better solution would be to rein in the cleverness and focus on readability rather than shortness.
- slavik81 9y agoI think it's related to the iron law of abstractions. It's easier to change code that has no abstraction than the wrong abstraction. The right abstraction is better than both those, of course, but is often difficult to identify upfront.
- mickronome 9y agoSuccinctly put. I have observed a tangentially related correlation: The code that needs least understanding to change will be the code changed, until it isn't the code needing the least understanding any more. In unison they sort of tells us why pragmatism is often good when we start writing code, but how we must always try to push towards finding (good) abstractions as the work progress.
- Ace17 9y agohttps://vimeo.com/9270320 https://vimeo.com/9270320 (Greg Wilson - What We Actually Know About Software Development, and Why We Believe It’s True). It seems we have scientific data showing that the number of code lines one can review per day doesn't depend on the programming language. This means one would spend a lot more time reviewing this C++ code if you looking at the manually-rewritten-to-assembly version instead. This is a very good argument supporting programming at the highest possible level of abstraction, which is the exact opposite of boilerplate and verbosity. So please let's not rationalize/stockholm-syndrome over the limitations of any programming language ; boilerplace and verbosity are always bad.
- phatoni 9y agoFrom the maintenance perspective I would prefer the more verbose code in most cases, because it is easier to debug. Moving the verbose code block to a method or function for reuse will fulfill the DRY principle as well. Then you can decide during debugging to step over or step into the method. Doing to much in one step would also contradict another rule: the single responsibility principle.
- grasleya 9y agoA counterpoint is that more declarative code can often remove the need for tedious step by step debugging because the behavior of the code is more transparent. I do most of my work in Haskell and find I don't typically need to debug it like I would for a less declarative language because the meaning of the code is much more clear.
- phatoni 9y agoI come from a C# background and have little experience with Haskell, so YMMV. C# got many new features over the years like LINQ or Lambda Expressions. My experience is that you get most of them if used in a moderate way. Like splitting complex LINQ into smaller parts and introduce intermediate results instead of one big query. They are also easier to test this way. Another important aspect of this topic is if you are maintaining your own code or code others have written. I used to maintain code written by another team, which only did feature development and barely any code maintenance. While doing only code maintenance on code written by others for a longer period I gained a new perspective on what's good code in the long term.
- chvid 9y agoMore bad excuses for not having exceptions in your language ...
- grasleya 9y agoThis is a hilariously bad take. If we follow the argument to its natural conclusion then go is itself a poor choice of language. After all, go removes memory management boilerplate in favor of an automated gc system. By the author's argument go should force you to manage your memory yourself. In fact, we really should all just code in assembly because it has the clearest relationship between code and what is going on under the hood. True, coding in assembly requires a lot of needless boilerplate but by this article's argument that's actually a feature. Go has so many truly bad design decisions (lack of generics being the most damning) and it's sad to see its fanboys try to argue that they're actually good.
- combatentropy 9y agoAll of the examples are far better than the code I normally deal with. That being said, the most tiresome code is this kind of step-by-step error checking. if (! is_numeric($id)) { exit('The ID is not a number.'); } $db = open_database(); if (! $db) { exit('There was an error connecting to the database.'); } $query = db_query($db, 'select * from widgets where id = $1', $id); if (! $query) { $db_error = get_db_error_msg($db); exit('There was something wrong with the query: ' . $db_error); } Zzzzzzz. It triples the code size. One third is the normal code path. Two thirds is all the error checking. I'm sorry, I just don't write it: $db = open_database(); $query = db_query($db, 'select * from widgets where id = $1', $id); I let errors bubble up to some general error handler, which stops the script in its tracks and gives the user a vague and unhelpful error message. It doesn't matter. Even if I gave them a very specific error message, all that they would do is log a ticket, and I would still go into the server log to uncover the line number the error was on, debug, and fix it. Don't get me wrong. I write JavaScript to constrain user input, with friendly directions and error messages. And I set tight types in the database and add database constraints where I can. But I don't write all this step-by-step checking in the middle layer (PHP, Python, Go, what have you). So if the user somehow bypasses the JavaScript and tries to insert text into an integer column, the database will simply refuse, and the resulting web page will be a very ugly error message, which is what they deserve. If the database is down, it's going to be ugly, regardless. I admit that this strategy works okay for non-life-threatening, SQL-backed web apps and such. You may need to do something tighter in your work. But I wonder if there would still be something more elegant than the every-other-line error checking shown in the article.
- grasleya 9y agoThe catch-all top level error handler isn't typically a hallmark of good design but I think it's pretty relatable as a quick fix when you need to get something working on a deadline.
- jstimpfle 9y agoThis is one of the situations where the difference between scripting and "real" development becomes obvious. Yes, it makes you zzzZZZz. If you don't care for errors anyway. However, when developing a large reliable software project you need to think through the possible error cases and handle them appropriately. Even if that just means logging them correctly and exiting so the error can be easily tracked down later (for example: was it expected/unexpected? What where the parts of the context that can't easily be collected by a runtime?). And in a large project, it's not that bad. How many places are there where you setup the database connection, an OpenGL context, a network connection, etc? Just build the abstractions appropriate for your project once. Simply crashing backwards through the function calls with a generic exception and a stacktrace is not always good enough.
- gravypod 9y agoI think most of the complaints about the python version go away when you do something like this.... is_even = lambda n: n % 2 == 0 score_result = lambda j, k: j is_interesting_result = lambda j, k: is_even(j) and k a = some_variable + 42 b = sum(map(score_result, filter(is_interesting_result, results))) or b = sum(j for (j, k) in results if is_even(j) and k) List comprehensions only become difficult when you start doing conditional execution within the result side. Use if for filtering or turn your complex operation into a meaningfully named function/lambda expression.
- Spiritus 9y agoThe first one is horrible and super un-pythonic (it even violates PEP8[1]). But perhaps even more important, it doesn't even work since a map-function can only take one argument (not two). Using functional programming paradigms like this often advised against in Python. [1] https://www.python.org/dev/peps/pep-0008/#programming-recommendations https://www.python.org/dev/peps/pep-0008/#programming-recomm... (search for "Always use a def statement")
- gravypod 9y ago> The first one is horrible and super un-pythonic (it even violates PEP8[1]). I'm not really concerned with PEP. I'm concerned with if the idea being expressed is cleaner then the original. My first iteration was cleaner then the original comprehension (despite the comprehension being PEP8). It also let me see what I was really doing and I was able to turn my map and filter into a much nicer list comprehension. Also, pointing out lambdas instead of defs is silly. It was done for an example. Most of the time you'll be operating on data structures with built in functions or supporting functions you will have written making it cleaner to use the map and filter paradigms. money = sum(user.get_payed_balance() for user in users if user.has_paid_bills() and user.is_still_subscribed()) bs clients = sum(map(User.get_payed_balance, filter(User.is_still_subscribed, filter(User.has_paied_bills, users)))) I much prefer the filters to the list comprehension in this case. I'll think "User's money for user in users if user has paid their bill and subscribed". I'm going to think "Sum the get_payed_balance for every user who is_still_subscribed and who has_paid_bills" for the functional implementation. If I had complex tuple arraignments then (like the OP's post) then I agree LCs will be the better way forward. If you're dealing with objects or functional-fitting problems I prefer map and filter. > But perhaps even more important, it doesn't even work since a map-function can only take one argument (not two). The concept still stands. Just use `from itertools import starmap`. Starmap is like map that calls the * operator on arguments. >>> a = lambda a, b: 10 >>> list(map(a, ((1, 2), (3, 4)))) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: <lambda>() missing 1 required positional argument: 'b' >>> list(starmap(a, ((1, 2), (3, 4)))) [10, 10] > Using functional programming paradigms like this often advised against in Python. I'm not concerned with if it's "Advised against". I'm concerned with a) will someone be able to understand this better then the implementation that currently exists) and b) will I be able to come back in 10 years and tell what this is doing if need be. You should check out this talk [1] before going around trying to use PEP8's inconsequential clauses to call something "horrible" code. [1] - https://www.youtube.com/watch?v=wf-BqAjZb8M https://www.youtube.com/watch?v=wf-BqAjZb8M
- moomin 9y agoThe proportionality argument is bunk, straw-man level bunk. Consider what goes on every time go accesses an object, casts to an interface or writes to a channel. Huge, complex code under the covers, way more than the Python examples he's given. What this article really says is "There are abstractions I'm not as comfortable processing as others, therefore those abstractions are bad." If having code be proportional to complexity was a good idea, we'd all be writing in assembly and subroutines would be frowned on.
- spc476 9y agoI have a Lua module I wrote that allow me to do stuff like: process.limits.soft.cpu = "4m" -- limit to 4 minutes of CPU time process.limits.soft.core = 0 -- and no core file Yes, ultimately, it ends up in setrlimit(), but the amount of code that goes into backing this up is a bit daunting. But it more than makes up for in showing the intent (and it's not like this is done in the hot path of a program).
- contravariant 9y agoI think having code be proportional to 'complexity' is good, but having it be proportional to 'work' makes no sense. Personally I find things 'simple' when they only require one level of abstraction, such as accessing an object, or performing arithmetic operations on algebraic objects (e.g. multiplying matrices). And complex when they require more (e.g. manipulating the coefficients of two matrices to define their matrix product). Boilerplate is when you requires more code than the complexity requires, usually because the language doesn't provide a sufficient opportunity for abstraction. Note that, if we define a '->' operator that roughly expands to the following: (x, err) -> f : if (err != nil) { return nil, err } return f(x); Then the entire code he wrote as an example of boilerplate can be written as DoSomething -> SomethingElse -> MoreWork; with some minor differences that could be optimized away in the right language.
- lacampbell 9y ago> "There are abstractions I'm not as comfortable processing as others, therefore those abstractions are bad." This is the design philosophy of Go, as far as I can tell.
- majkinetor 9y agoI always write terse code, not only because it reduces the number of lines (which is important to me) but also because: - You clearly state the atomicity, that this thing happening in 1 long line is specific thought, intent, desire - it shouldn't really be on multiple lines - its easier to remove/comment out intent (as it is always self contained), replace it, optionalize it and reuse it. - Scrolling the code is damaging to the focus. - Once you learned the specific methods used to create terse code seeing it repeated all over again you will become more proficient in reading the code all around. - Its way more fun. Its like poetry in programming - why would we all write dummy boring stories. Language is everything (obligatory babel 17 mention: https://en.wikipedia.org/wiki/Babel-17 https://en.wikipedia.org/wiki/Babel-17)
- elgenie 9y agoThe title is significantly more ambitious than the content. I think "Go: I have found my Blub" would be more accurate.
- hacker_9 9y agoYes but it is avoidable if you use a Lisp. Every part of the code can be compacted into a smaller form. I know when I write in Clojure it's always the most concise code I've ever written, with minimal effort too.
- _asummers 9y agoThis is true in Elixir as well, which has hygienic macros very much like a Lisp. There's a balance, of course, but it really is quite nice to be able to just rewrite the AST as you need. And one must always remember the adage data > functions > macros.
- maehwasu 9y ago"Man inadvertently discovers reason for monads and strong typing while writing Medium post"
- shazow 9y agoHey HN, I wrote this post. I'm getting the sense that not a lot of commenters are taking the time to read the contents before reacting to the title, so I'll share a little spoiler/TL;DR: Good boilerplate is boilerplate that usually gets changed over time (and in different ways each time) as we iterate over the code. It took me a while to come to this realization (and I explain the reasoning and consequences in the post), so I hope it's helpful to other people who might not have found this intuitively obvious. Also perhaps "terse" or "clever" are not the best words to describe the counterpoint—I welcome edit suggestions.
- chatmasta 9y agoI would add, code that changes over time but not often enough to warrant a new abstraction. If you're only using the boilerplate for a new project every few weeks, it's probably overkill to set up some abstraction with variables and templating language. Granted, I'm using "boilerplate" here in a more general sense than you did in your blog post.
- userbinator 9y agoI'm getting the sense that not a lot of commenters are taking the time to read the contents before reacting to the title As evidenced by none of the discussion hitherto noticing that your Python and Go examples actually perform a slightly different process? Was that deliberate? ;-) (I rarely use Python and never Go; yet I noticed quite easily that the Python code is summing the selected odd elements, while the Go code is summing the selected even elements.)
- qb45 9y ago> As evidenced by none of the discussion hitherto noticing that your Python and Go examples actually perform a slightly different process? Was that deliberate? ;-) Good catch. I like abstract, declarative and structured code because if nothing else, it requires some level of engagement to write which has a side effect of reducing silly mistakes.
- shazow 9y agoGood catch indeed! Maybe I should keep it as-is for fun. :) Edit: I changed it.
- addicted 9y agoI have no idea what the Go code does, but the python code is immediately clear. It reads like a line of English. I can't see how anyone could prefer the Go version. And I don't know if python allows this (it may not due to the significance of whitespace), but your proportionality problem can be easily resolved by placing each different clause on a different line, like this: sum((j if j % 2 else 0) for (j, k) in results if k)
- wayn3 9y agothis is what happens when people who "dont need math to program" write code. if you want to sum over even numbers, just do: sum([j*2 for j in range(0, end_range/2)]) and yes, brackets supercede pythons "whitespace is syntax". if you need linebreaks where, ordinarily, linebreaks are not allowed, just write brackets.
- odonnellryan 9y agoThat's silly! Programming is more than that. This is best: sum(range(0, end_range, 2)) ;)
- wayn3 9y agoi dont know if thats better. but both solutions are a lot better than a statement that contains about half a dozen implied negations.
- odonnellryan 9y agoWhy don't you think it is better? Easier to understand. I was mostly joking, but I think it's a little unfair to imply people who are not great at math are bad programmers.
- wayn3 9y agothey are certainly bad computer scientists, since its a subdiscipline of mathematics. can they be great artisans? sure. why do i think its not better? i said im not sure. i think the two options are about equal in readability, up to taste. yours is probably more efficient in terms of cpu cycles.
- coldtea 9y ago>In Python (and many other languages), it’s very easy to unintentionally hide tons of work in a single line of built-in syntax. Some lines do almost no work, other lines do a moderate amount of work, other lines do what I’d describe is tens of lines worth of work—a lot of work. It varies a lot, even in beginner code. In Go, I’ve found that this variability is much more diminished. I call BS. Consider these 2 Go lines: heck_if_I_know_what_this_does() heck_if_I_know_what_this_does_either() Just because Go has less powerful primitives, it doesn't mean that you don't use opaque code, libs, etc. all the time. Sure, you can look into what each function does -- but then again, you can also immediately tell that a list comprehension in python is doing at least O(N) work compared to a simple addition, without even having to open any other source file. >Is this good? I believe it is. I believe this code is more proportional to the complexity of the work that it’s doing. We invented programming languages on top of machine code so that our code can be LESS proportional to the "complexity of the work that it’s doing". >When reading proportional code, it’s easier to notice where the interesting bits are. Not really. It's the inverse: the terse code only keeps the interesting bits. The "proportional code" gives you all the non-interesting, and getting in the way, mechanics. >When a friend was reading through the source code for ssh-chat, he was surprised that I implemented my own Set type. I explained that while Go doesn’t have a built-in Set, it’s just a few lines of boilerplate to make your own on top of a map. In fact, I noticed that my version of Set evolved to be fairly specific to how it was being used. In retrospect, I’m glad that I was tweaking my own implementation iteratively rather than spending time working around whichever limitations a generic library might have had. Pray tell, what "limitations" a generic Set would have? And why does those kinds of post remind one of Stockholm Syndrome?
- flavio81 9y agoI think you have discovered and defined the "Stockholm syndrome for programming". Indeed the author is suffering from it.
- taneq 9y ago> Just because Go has less powerful primitives, it doesn't mean that you don't use opaque code, libs, etc. all the time. True, but that's not what the article's arguing. > We invented programming languages on top of machine code so that our code can be LESS proportional to the "complexity of the work that it’s doing". No, we invented programming languages so that our code can be smaller and less arcane, not less proportional. The actual point of the article, which I believe you missed, is that if we focus on 'cleverly' compressing large amounts of logic into dense nuggets of code, it interferes with our ability to appropriately factor the code so that it produces the desired behaviour in the simplest and most readable way. The article also states (and I agree) that if your code is wildly variable in the amount of semantic content per line of text, it's much harder to properly grok and (more importantly) to modify what's going on.
- flavio81 9y agoIn the article: I was expecting a discussion about boilerplate but the author writes about how ugly/bad is code that does a lot of operations in one line. Then he introduces the concept of "proportionality", and according to it, assembler must be the best language since the program statements are directly proportional to the operations to be performed... Suddenly high level programming is no longer good stuff... Meanwhile no real discussion about boilerplate!! Whenever your code shows implicit patterns that get repeated but cannot be abstracted away using code, you have boilerplate and boilerplate is almost always a bad thing. Your choices are then: 1. Pretend those patterns as a good thing. 2. Switch to a more powerful language and destroy the boilerplate either because it isn't necessary anymore, or because you can abstract it away with, for example, a macro.
- shurcooL 9y ago> Then he introduces the concept of "proportionality", and > according to it, assembler must be the best language since > the program statements are directly proportional to the > operations to be performed... > > Suddenly high level programming is no longer good stuff... Looks like you didn't understand what he meant in that section if you think he implied Assembly is better than high level languages. It's not that 1 line of code should do exactly 1 unit of work, but rather that if 1 line does 100 units, then 10 lines should do 1000. Function calls are exempt from that, because everyone understands that a function can do arbitrary amount of work, and one needs to know what it does (by reading its name and documentation). The cost is considered to be a "function call". Yes, Assembly also has high proportionality, which is a benefit to it. But it doesn't mean all other languages are bad. These are tradeoffs. The point is that it's a beneficial property of a language when you have code length that better corresponds to the amount of work it's doing.
- flavio81 9y ago> but rather that if 1 line does 100 units, then 10 lines should do 1000. Why they should? It has no reason to be that way. If anything, 1 line should do 1000, if possible. Some lines will do 1, others 10, others 5, others 100. "Proportionality" adds no value and, quite the opposite, discourages programming at the highest level possible.
- KirinDave 9y agoCall me old fashioned, but I remember when encapsulated was lauded as a good thing because it insulated the programmer from whatever programming model was considered popular this year was a virtue.
- logn 9y agoYou can write code like Hemingway (simple) or like Shakespeare (dense). Hard to say whether one is better than the other without some attempt to quantify and measure things. Seems more like just a preference. In regard to boilerplate the author brings up an interesting point that with a good language, the boilerplate can effectively disappear once all the code has been written. If people are worried that boilerplate violates DRY, I think they worry too much. The best way I've found to determine whether a codebase is good or not is to take someone who's never worked with the code before and ask them to fix a bug or make an enhancement. If that's an excruciating task but would have been easy in a greenfield project, the code sucks.
- thescribe 9y agoI find the number of mistakes I made scales at least linearly with the number of lines I write. Boilerplate is bad because it allows me to write more.
- Tarean 9y agob = sum((j if j % 2 else 0) for (j, k) in results if k) This definitely is borderline unreadable but that isn't necessarily because it does a lot of things. It is because it does a lot of things without order and with a lot of line noise. Compare it with sum [j | (j, True) <- results, odd j] or filter snd >>> map fst >>> filter odd >>> sum The bigger problem is that this carries a bool around. If it is only used for filtering filter it beforehand. The Opportunity for correction is an interesting idea but the authors issue mostly come from a lack of referential transparency. In a pure language refactoring wouldn't be an issue so one could argue this is a symptom of too little abstraction, not too much.
- skrebbel 9y agoIf you'd name your variables all three would be perfectly readable I bet.
- Tarean 9y agoTechnically the third has no variables... Slightly less wise-ass, I couldn't come up with a use case for this code that'd give sane variable names. Third attempt: sum $ do (element, True) <- results guard (odd element) return element That's pretty far from imperative languages, though, which I kind of tried to avoid.
- buttershakes 9y agoIt's not true for all languages, but I actually like Erlang's boilerplate. Almost every OTP project has the same structure, and a very close set of primitives that drive the setup and structure of the application. It can be annoying to setup and get right, but its a common set of structure and patterns which make it much easier to dive into a large project. So for me it is not always bad, just usually, with some specific exemptions that I think are done well.