I’ve read a bunch of these articles on abstraction, and if it’s harmful or not, and I have a very difficult time understanding why abstraction seems to be such a difficult and contentious problem.
I think part of the problem might be that many people seem to confuse abstraction with making something easy. So they make a library that makes the 90% case a one liner, but deviation from that is extremely painful. That is not finding the essence of something.
Creating a good abstraction requires a lot of creativity and knowledge. It’s hard, and requires experience and knowledge. One has to be able to realize that two things are actually the same thing in disguise. That is what abstraction in a language like Haskell is like. You realize that the problem you have is a monoid, or a monad, or an applicative, and then you apply the rules that gives you because knowing it’s one of those things gives you power to understand its limitations. that’s vastly different then what I feel I see in other languages, where abstraction comes down to a pattern but it’s unclear if that pattern actually tells you something about the problem its solving. Consider seeing that the solution to a problem is a monoid vs if it implements an observer pattern. The monoid tells you something about the problem. The observer tells you something about the solution. I’m making this up as I go, so perhaps someone can call bullshit on me.
I think part of the problem might be that many people seem to confuse abstraction with making something easy.
That sounds right to me: I often hear “I simplified this” or “I abstracted that” to basically mean “I created a function/class that takes a couple of parameters and does the thing”. I won’t go into “simple vs. easy”, I think Rich Hickey did a wonderful job of distinguishing those two concepts; I’d love for someone to do a similar essay/talk on “abstraction vs. packaging”. In CS, we see many abstractions (e.g. context-free grammars, finite state machines, functors, monoids, etc.) and we learn about their properties. For instance, we learn that the union of two regular languages is a regular language or that the mappend operation in a monoid is associative. When someone shows me their “abstraction”, I’m pretty sure there are no properties to speak of: it’s just putting a bunch of statements inside a package. One may call that an abstraction, but it’s pretty concrete because it refers to only one single thing.
I think of it as the difference between abstraction and encapsulation.
Most of what we do in day to day dev is encapsulation. And that’s ok!
I don’t think encapsulation is OK! It often breaks code reuse. Edward Kmett has something useful to say about this topic: https://youtu.be/yFXzuCFeRGM?t=1h36m55s
I have a “real world example” of this from recently as well that I think illustrates a strength in not encapsulating things. I recently wrote a small build system for Ocaml that met my requirements on simplicity and configurability. It worked by simply taking some configuration and directories and outputting Makefile’s to build the directories. I could have decided to hide that, but instead I exposed that it was actually just generating Makefile’s. There is even a configuration option to add lines to a Makefile. The result of this is my little tool works with zero effort on 90% of libraries I write in Ocaml, but when I hit a more complicated build (for example building some C libraries and bindings to them which require a bit of a Rube Goldberg setup) the system does not get in the way at all, I simply add my extra Makefile rules. It’s been nice to not have to change build systems just to do something more complicated than the simple path I wanted to natively support.
Hickey’s talk is full of quite a few ideas. How would you summarize “simple vs. easy”, w.r.t. abstraction?
(spoiler alert - you might want to write up your answer before reading mine)
Definitions: a simple abstraction is one which is transparent in an unsurprising way, i.e. it’s there when you don’t need to think about its details, but the details never come as a surprise. The ease of a solution is a measure with a few more inputs, involving the programmer and the abstractions they are familiar with.
Answer: Given a problem, then, one might need to make the choice of finding a solution that’s easy (i.e. using abstractions you are familiar with) or simple (i.e. finding the best abstraction). Given a real-world team and a real-world problem, this choice itself becomes non-trivial.
You are spot on that abstraction is a building block for thought, and the set of abstractions one chooses result in a different formalization to the solution. So it’s actually really important to pick the appropriate abstractions to minimize incidental complexity in said formulation.
I guess the maxim is:
Every design decision is a tradeoff, abstraction is a design decision. Acknowledge your tradeoffs.
He quoted Djikstra, but I think fuller quote may be more useful…
So here it is…(Emphasis mine)
Argument four has to do with the way in which the amount of intellectual effort needed to design a program depends on the program length. It has been suggested that there is some kind of law of nature telling us that the amount of intellectual effort needed grows with the square of program length. But, thank goodness, no one has been able to prove this law. And this is because it need not be true. We all know that the only mental tool by means of which a very finite piece of reasoning can cover a myriad cases is called “abstraction”; as a result the effective exploitation of his powers of abstraction must be regarded as one of the most vital activities of a competent programmer. In this connection it might be worth-while to point out that the purpose of abstracting is not to be vague, but to create a new semantic level in which one can be absolutely precise. Of course I have tried to find a fundamental cause that would prevent our abstraction mechanisms from being sufficiently effective. But no matter how hard I tried, I did not find such a cause. As a result I tend to the assumption —up till now not disproved by experience— that by suitable application of our powers of abstraction, the intellectual effort needed to conceive or to understand a program need not grow more than proportional to program length.
So there we have why we need abstraction, and what property is required of our abstraction.
A large class of “Technical Debt” (buzz phrase for kludges) is where someone, with full knowledge, breaks the abstraction as a quick fix as it won’t hurt him right now….
Leaky abstractions considered harmful.