Re:On DRY and the cost of wrongful abstractions
by dboreham on Sunday September 18, 2016 @05:47PM
(#12527362)
>Needing to bounce all over the codebase to figure out what those little functions are actually doing is hugely distracting while I'm trying to read.
Ideally those functions should be named such that their jobs are fairly obvious.
Re:On DRY and the cost of wrongful abstractions
by josephg on Sunday September 18, 2016 @06:10PM
(#12527462)
Sure, but that totally depends on context. A function called `renderFooter()` is great, even if its just 1 line. I know exactly what thats going to do.
But a call to `setImpulse(spaceship, 0, somevar)` might just set member variables ximpulse and yimpulse, or it might make a bunch of calls to the physics engine, or both, or something else entirely. If I'm trying to understand some code that calls setImpulse I'll probably end up looking it up just to be sure. So in that case, if it does just set the member variables I'd prefer to just write `spaceship.ximpulse = 0; spaceship.yimpulse = somevar;`.
Same thing, but less work to read.
Re:On DRY and the cost of wrongful abstractions
by rocqua on Sunday September 18, 2016 @06:11PM
(#12527464)
It's the difference between reading code to get an intuition of what happens, and reading code to debug an issue.
For the first, well named single line functions are great. For the second, they require a lot of jumping around.
As the article mentioned, the thing you spend most of your time doing is reading. Using lots of small functions means you have to bounce all over your codebase to figure out whats actually going on. In procedural languages a complex 'meaty' function often needs to be understood in one conceptual bite. Needing to bounce all over the codebase to figure out what those little functions are actually doing is hugely distracting while I'm trying to read.
I often start those big functions with a block comment explaining what I want the computer to do ("// The overall goal is X, which needs Y and Z to be true. There are 4 cases to consider: A, B, C and D through which these invariants need to be true..."). Then I write the code. The code itself will be delineated with comments calling back to the documentation ("// Case B - the wendigo. Note we're being careful here to make sure that x is always < y"). If I find myself duplicating blocks inside that function, I'll pull those shared blocks out. But single lines? When I'm reading the code later I'm going to want to track exactly what happens through the function at a mechanical level. I want to be able to see the invariants, and track exactly how each value flows through. And for that, the less scrolling and bouncing around my codebase I need to do the better.
Often separate cases are cleanly distinct enough that it makes sense to extract them into their own functions, but sometimes they're not. And thats ok too.
In a sense you want to split things up based on how you expect to read it. If I have lots of tiny functions that are only used once or twice, I'm probably going to forget what they actually do and need to read them to understand the code that calls them. In that case, the code should just be inlined.