3 ms·
> The actual operation being defined works on the same level of abstraction, since it defines precisely the same operation. Agreed. This confusion is something
by MaxBarraclough 6y ago
> The actual operation being defined works on the same level of abstraction, since it defines precisely the same operation.
Agreed. This confusion is something Zed Shaw wrote about in blog post called Indirection Is Not Abstraction [0]. Surprisingly it seems it was never discussed properly on HN [1], but it was discussed elsewhere [2][3].
(There's also another blog post by this name by another blogger, by independent reinvention/coincidence [4].)
> Young engineers should not be taught to fear "over-abstraction".
Disagree. As you just showed, unnecessary abstraction is bad. Ineffective abstractions are also bad. It's not easy to get right.
[0] https://web.archive.org/web/20160304022133/http://zedshaw.com/archive/indirection-is-not-abstraction/ https://web.archive.org/web/20160304022133/http://zedshaw.co...
[1] https://news.ycombinator.com/from?site=zedshaw.com&next=8820179 https://news.ycombinator.com/from?site=zedshaw.com&next=8820...
[2] https://www.reddit.com/r/programming/comments/38hobm/zed_shaw_indirection_is_not_abstraction/ https://www.reddit.com/r/programming/comments/38hobm/zed_sha...
[3] https://lobste.rs/s/ja5ihv/indirection_is_not_abstraction https://lobste.rs/s/ja5ihv/indirection_is_not_abstraction
[4] https://news.ycombinator.com/item?id=18344033 https://news.ycombinator.com/item?id=18344033
- gen220 6y agoThank you for these excellent links. This debate often takes the concrete form of “one large imperative function with 100 lines” vs “5 functions with 20 lines”. Many people who separate out logic for the sake of minimizing lines-per-function unfortunately do so by introducing indirection, rather than abstractions, and thereby make the program more challenging to reason about and test. But, the reason the debate is never-ending is because the question isn’t sufficiently defined! :) I like to think of a program like a tree (main is root, each function is a node, calls are edges). Each sub tree should be a bounded context, in that (ideally) you only have to think about parameters defined in that sub-tree. Leaves implement the “nitty-gritty” (mainly I/O, number-crunching, and complex transformations), and are heavily tested. Each node that isn’t a leaf is either an abstraction composing leaves, or an abstraction composing abstractions. Unit tests for leaves test the nitty gritty, unit tests for non-leaves must only test composition. I find that human-readable modules have some limits (number of children, height of the tree). You can violate those limits sometimes, but only if you provide some assistance in the form of comments. Sometimes, a 100-line function is not composing many distinct nitty-gritty ideas. It just really takes 100 lines to express “write this model to the database”.
- MaxBarraclough 6y agoI agree that relatively large functions aren't always an evil. As you say, sometimes there isn't a tidy way to further decompose it. At the same time though I don't think it's always a sin to write a function just for decomposition, without it doing any abstraction. A 'helper function' might be tightly bound to some other function, i.e. the helper function is sensitive to the internal workings of the function it serves, and is not intended to be called from anywhere else. And there's still no excuse for source files that are 5000 lines long, of course. HN discussion of John Carmack's thoughts on how long functions are sometimes preferable: https://news.ycombinator.com/item?id=8374345 https://news.ycombinator.com/item?id=8374345