Clojure Gazette 175: Can larger abstractions help more than they hurt?

Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.


Can larger abstractions help more than they hurt?
Clojure Gazette
Issue 175 - May 30, 2016

Please consider advertising in the Gazette.


Hi Clojurists,

Remembering our call graph/tree, we noted that we should have a deep tree in order to maximize the contribution of each line of code. This brings up the question of whether a wide tree would help us increase the power of each line.

In fact, I think it would. If each function called more than two other functions, a single line of code can achieve more. If the branching factor is b and the depth is d, then one function call will call b^d (b to the dth power) system calls. If we have to make 1000 system calls, we can do it with 1000^1, 10^3 or 2^10. Increasing b or d will increase the number of leaves called.

However, increasing the branching factor has other costs. Remember, we started this exploration talking about the riskiness of abstractions. We concluded that smaller abstractions were less risky. There's a direct tradeoff between branching factor and risk. If we want to reduce risk, we have to decrease the branching factor. So where should we set the balance? Is this a devlish tradeoff with no good resolution? I don't think so.

Decreasing the size of an abstraction makes it more reusable and increases d, the depth of the tree. Increasing the branching factor b does help increase the power of one line, but it makes everything else worse. It decreases d and reusability. And most damning is it increases the risk of each abstraction.

They're at once more expensive to write and more likely to be wrong. Luckily, the power of exponential growth comes to our rescue again. Even a small average branching factor will have exponential growth, which will eventually overtake the linear overhead of each abstraction. An average branching factor of 1.01, on a large enough project, will be able to take advantage of the exponential overtaking the linear.

Everything still points in the same direction: smaller abstractions that call each other. They're more reusable, cheaper to throw away, and more likely correct. They also help us decrease the size of our code. But if we're increasing the depth of the tree, that means we're making more function calls and more stack frames. Does this increase have a negative impact on runtime performance? Tune in next time and we'll explore that angle.

Rock on!
Eric Normand <eric@lispcast.com>

PS Want to get this in your email? Subscribe!
PPS Want to advertise to smart and talented Clojure devs?