Discussion on: What is an abstraction?

kspeakman profile image
Kasey Speakman

I think one of the biggest reasons abstractions are leaky is because they are too prescriptive. E.g. Inherit from this class. Ok, which methods do I have to override to achieve what I want? Do I need to call base or not? How does this affect other properties of this class? What if the interface is synchronous, but I need to do some async work and it should not be blocking? So the author of this abstraction either has to make all these choices for me up front (sometimes called "opinionated") or has to provide parent class/method permutations for a lot of cases. Or it uses something like Reflection to adapt to many different cases. Either way exposes me to implementation details (as conventions) and limits me down to whatever fits in that box.

The most useful abstractions are ones that expose the operations you can perform and let you opt into and compose only the ones you need. When the abstraction is at the edges of a program (e.g. web server), it should stay as course-grained as possible. A good example of this is the Suave web server. The simplest request handler takes a raw HttpContext and returns one (optionally and asynchronously). You can handle the request absolutely any way you want or ignore it. But Suave has opt-in helpers for common scenarios like GET to handle only HTTP GET requests to a specific URI or setHeader, etc. These helpers are composable, and you can make your own. This abstraction places few limits on me, and I have the option of not learning/using the built-in helpers if I so desire.

ericnormand profile image
Eric Normand Author

Hey Kasey,

Yeah, great examples.

We tend to call those things "abstraction". And they are in the Sussman sense since we're naming a compound element. But it's not an abstraction in the Dijkstra sense, because it's not really making it so you can be totally precise. Or at least it's hard to make it precise. You're telling the user of your library that they can subclass this thing but they have to follow all of these poorly-defined rules. To make it less leaky, you should be able to limit the number of rules you have to follow, or at least make them automatically checked.

There are also leaks just from oversights. One of the massive oversights of many languages is allowing a Null Pointer. Now there's this mandatory check after every method returns. Do I actually have the thing the type told me or do I have null? People talk about the cost of Null Pointer exceptions in production. But what about the cost of adding an if statement after every method call? When we design an abstraction, we have to think hard about introducing corner cases and avoid them at all costs.

Rock on!