3 ms·
The problem is that it's easy to overdo it. When you are writing the code, you already know what it's supposed to do, and adding a few more things to the chain
by jltsiren 2y ago
The problem is that it's easy to overdo it. When you are writing the code, you already know what it's supposed to do, and adding a few more things to the chain is convenient and attractive. But when you are reading unfamiliar code, you often wish that the author was more explicit with their code. Not just with what the code is actually doing, but what it's trying to do and what are the key waypoints to get there.
With procedural code, it's widely accepted that you should not do too many things in a single statement. But in functional code, the entire chain is a single statement. There are no natural breakpoints where the reader could expect to find justifications for the code.
- stouset 2y ago> The problem is that it's easy to overdo it. Welcome to all features of every programming language? Sacrificing readability, optimization, and simplicity for the 95% case because some un-principled developers might overdo it in the 5% case (when the cost of fixing it is trivially just inserting variable assignments) is… not a good trade-off.
- jltsiren 2y ago5% is common enough that you'll encounter it almost every time you read code. And fixing it is not easy, because you first need to understand the code before you can add useful variable names. Besides, programming language evolution is mostly driven by the fact that everyone is lazy and unprincipled at least occasionally. If you need to be disciplined to avoid footguns, you'll trigger them sooner or later.
- stouset 2y agoThe cost of this "footgun" is basically zero. Every step in a functional pipeline is isolated and wholly independent. If you want to split such a pipeline in two, doing so is trivial.
- whstl 2y ago5% is also low enough that you can just use another technique for the exceptions.
- vendiddy 2y agoIt's not clear what point you are trying to make because so far you are describing problems common to all programming languages.
- whstl 2y ago> But in functional code, the entire chain is a single statement Not necessarily. You can use intermediate variables when necessary.
- syklemil 2y ago> But in functional code, the entire chain is a single statement. There are no natural breakpoints where the reader could expect to find justifications for the code. How are we deciding what's "functional code", here? Because functional languages also provide means like `let` and `where` bindings to break up statements. The example might in pseudo-Haskell be broken up like distinctAuthors = distinct authors where authors = map (\book -> book.author) longBooks longBooks = filter (\book -> book.pageCount > 1000) books IMO the code here is also simple enough that I don't see it needing much in the way of comments, but it is also possible and common to intersperse comments in the dot style, e.g. distinctAuthors = books // TODO: Where does this collection come from anyway? // books are officially considered long if they're over 1000 pages, c.f. the Council of Chalcedon (451) .filter(book => book.pageCount > 1000) // All books have exactly one author for some reason. Why? Shouldn't this be a flatmap or something? .map(book => book.author) // We obviously actually want a set[author] here, rather than a pruned list[author], // but in this imaginary DinkyLang we'd have to implement that as map[author, null] // and that's just too annoying to deal with .distinct()
- bonoboTP 2y agoIf you're used to it, then it doesn't read like a single statement, even though technically it is. You put each call of the chain on its own line and it feels like reading the lines of regular imperative code. Except better because I can be sure that each line strictly only uses the result of the previous line, not two or three lines before so the logic flows nicely linearly.