3 ms·
I can't help but wonder if we're sometimes using the wrong words for things. In this discussion we keep talking about "code duplication" and "abstraction" hand-
by ragnese 6y ago
I can't help but wonder if we're sometimes using the wrong words for things. In this discussion we keep talking about "code duplication" and "abstraction" hand-in-hand, but I think they're almost orthogonal concepts, at least as I think of them.
Seeing the same code almost copy-and-pasted in a few places might call for some code-deduplication. But that's not necessarily a new "abstraction" in my eyes. It may be, but it also may not be.
I'm struggling to think of a specific example because I fully intended to go to bed before arriving here... But as a really stupid example, let's say you have `val a = x + x + x` and `val b = y + y + y` and `val c = z + z + z` in your code. If you write a new function like `fun addThreeTimes(i) = i + i + i`, I don't see that as a new abstraction at all. If, however, you invent multiplication, now you're at a new abstraction! `val a = x * 3; val b = y * 3`, etc.
"Abstraction" to me is about thinking at a different semantic level, not about avoiding copy and paste.
Does this resonate with anyone else? Am I missing the point?
- lambdatronics 6y agoI would say it becomes an abstraction when it needs parameters in addition to the nominal arguments. So in the case of "addThreeTimes(i)", sure, that's just abbreviation, but "addNtimes(i,N)" is a bona-fide abstraction. Edit: Reminds me of the distinction between inheritance and subtyping: https://www.cs.utexas.edu/users/wcook/papers/InheritanceSubtyping90/CookPOPL90.pdf https://www.cs.utexas.edu/users/wcook/papers/InheritanceSubt...
- ragnese 6y agoI agree with your point and your edit. The inheritance thing is very similar to what I'm talking about!
- epage 6y agoIn my experience, code dedupping without shared requiremeots hurts you over time. Nothing is keeping the code focused so as requirements change, you either fork it or add "one more flag". Focusing on abstractions is what helps me in dedupping with a focus on requirents.
- MaulingMonkey 6y agoThey're theoretically orthogonal but practically not. You can deduplicate code without abstraction per se, but the result is generally unreadable and unmaintainable. As such, all reasonable code deduplication relies upon abstractions. However, not all abstractions involve code deduplication, and may instead have other goals (such as making it easier to reason about local state, invariants, etc.) > If you write a new function like `fun addThreeTimes(i) = i + i + i`, I don't see that as a new abstraction at all. If you only call it once, it's not code deduplication either. What differentiates addThreeTimes(i) from sqrt(x) or average(x,y) or pow(x,y) or multiply(x,y)? Not how many call sites it has, nor the presence of a dedicated operator to the function in the language. Instead, I'd say: the function's reusability, composability, commonness, ... or to put it another way: addThreeTimes is an "abstraction" - it's just a poor garbage unreusable unremarkable unrememberable abstraction with no expressive power. However, poor abstractions aren't the only result of overeager code deduplication. Sometimes you end up with "good" abstractions misapplied to the wrong situations - e.g. they solve issues your current problem doesn't actually have. As an example, turning your list of game entities into a list of (id, aabb_f32) tuples might be exactly what you want for a renderer culling or broad phase physics pass - but completely counterproductive for implementing the gameplay logic of a turn based game! If you've already got a list of tuples, you've a few choices: 1. Modify the tuple (add tile position information that's useless to the renderer/physics, muddying the abstraction) 2. De-abstract (e.g. perhaps change several function signatures to pass in the original entity list instead of the AABB list) 3. Re-abstract (perhaps your gameplay logic should take something else that accounts for things like the fog of war instead of a raw list of entities?) 4. ???
- ragnese 6y ago> What differentiates addThreeTimes(i) from sqrt(x) or average(x,y) or pow(x,y) or multiply(x,y)? Not how many call sites it has, nor the presence of a dedicated operator to the function in the language. Instead, I'd say: the function's reusability, composability, commonness, ... or to put it another way: addThreeTimes is an "abstraction" - it's just a poor garbage unreusable unremarkable unrememberable abstraction with no expressive power. I agree that call sites or presence of language operators is not the defining distinction here. But I disagree that reusability, composability, or commonness (is that not "call sites"?) are somehow defining features of an abstraction, either. Obviously, those are good qualities for code to have, but that's not related to what I'm thinking about. The difference in my example is specific to the ladder of abstraction from addition to multiplication. When I was taught multiplication in early grade school, I was taught it as basically just being another way to write addition. When I first learned it, I would do exercises that involved taking an expression like "3 * 5" and translating it to "3 + 3 + 3 + 3 + 3" and then evaluating that. However, after time, I've stopped thinking about multiplication as addition. In my mind, I just think of multiplication as its own thing. I've fully internalize the "abstraction" because I don't even think about addition anymore when I see multiplication. So, when we take a Year, Make, Model, and Color and group them together and call it "Car", we're making an abstraction and it has little to do with code duplication. It has much more to do with wanting to think about higher-order constructs. You and I agree here, as per your first paragraph. If I have some kind of rendering engine and I find myself often rotating, then shifting a shape, I can write a `rotateThenShift(Shape, angle, distance) -> Shape` function and not feel like I've abstracted anything. I'm still "talking" about a shape and manually moving it around. Even if I just rename that function to `foobinate(Shape, angle, distance)`, I feel like I'm closer (but not quite) to a new level of abstraction because now I'm talking about some higher-order concept in my domain (assuming "foobinate" would be some kind of term from geometry that a domain expert might know). All other points about good or bad abstractions apply. I just don't think every single function we write is a new abstraction.