4 ms·
I think this definition of abstraction is lazy. Abstraction is indeed hard to define precisely, but it may be more accurate to describe it as the process of imp
by foxtr0t 6y ago
I think this definition of abstraction is lazy. Abstraction is indeed hard to define precisely, but it may be more accurate to describe it as the process of implementing interfaces that are conceptually familiar to users, often through metaphor, such as the unix "pipe".
Creating the "right" abstraction is _not_ the process of bundling up repeated code and only questioning how "abstract" it should be, it is the process of creating an interface that is familiar and conceptually easy to grasp. This is done through naming, comments, the use of metaphors, etc. From this viewpoint there can be many correct levels of abstraction, some more useful than others. We should aim to create good abstractions at all layers of code, even if finding abstraction bliss is unattainable.
- kqr 6y agoAs Dijkstra put it in the '60s: we start with hardware, which is fully capable of solving our problem, but not designed to make it convenient to express that solution. So we make "virtual machines" by creating new "instructions" that extend the physical machine. (Dijkstra called them virtual machines; these days we might say APIs or something.) We keep doing this: extending level n-2 into level n-1, with the guiding principle that n-1 should be a "virtual machine" that makes it slightly more convenient to express level n. At some level k, the solution to our original problem is trivial. Other important properties of these abstraction levels is that - They should do resource allocation and management to the point where a raw resource used by level n should not be visible as such in level n+1. - Any level should ideally only depend on one level below it. - No level can ever depend on a level above it -- this creates cycles in the dependency tree and prevents further extension and contraction of functionality. ---- Drawing further from Parnas instead: abstractions should hide design decisions that are likely to change. Typical examples of those are the format of data, layout of data structures, implementation details. Things that are less likely to change are things that come from the problem domain. Design interfaces based on problem domain concepts, not solution domain concepts. This applies at every level: the abstractions of level n-1 should have interfaces in terms of problem domain concepts of level n.
- blitmap 6y agoThere are things in here I had not considered before. I often abstract away to make something "easier for me" to think about, but I rarely consider "what is likely to change" and hide that behind a layer. Thank you for sharing.
- kqr 6y agoBonus tip: any time your team has a heated argument about any decision, that is a strong contender for abstraction.
- wh33zle 6y agoAs in, create an abstraction so everyone gets to do it their way and you don't have to debate?
- vardump 6y agoI think more like something that gives rise to an argument might be more multifaceted issue than it seems like to any given single participant?
- ptx 6y agoOr the heated debate might be an indication that it's not clear which choice is the right one, so we use an abstraction to make it easier to change our minds later if it turns out our choice was wrong.
- cinnamonheart 6y agoOne of my favourite quotes on the topic of abstraction comes from Dijkstra, too. > The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise.
- kqr 6y agoYes! And this also ties in to the notion that abstractions should encapsulate invariants.
- kortilla 6y agoYou’re overly restricting the process of abstraction to making it conceptually easier to grasp for users. That’s only one use case for abstraction. Let’s call it dumbing down for your external callers. There is also developing abstractions for south-bound APIs where it goes the opposite way. I declare that I want to be able to do operations X, Y, and Z but I don’t care how it happens. Then an implementer will be the one who knows all of the details specific to their implementation even though I’m the one who “abstracted” by defining the abstract API signatures. > it is the process of creating an interface that is familiar and conceptually easy to grasp Sometimes it’s not though. Often times the “familiar” and “easy to grasp” is the API burdened by legacy systems tied too much to one implementation. In this case the process of abstraction is cleanly separating the intent from the legacy crap and defining a new interface that captures the intent and nothing more (not leaking the details of the “how”). An API that writes bytes to an arbitrary location on a disk is certainly familiar and easy to grasp. But without a filesystem abstraction on top of it, it’s not really useful if you want portability, multiple processing using a disk, etc.
- kqr 6y agoI think your first point is really about the same thing, only taking a top-down design approach instead of bottom-up. The top-down approach is good in cases where you're confident in your ability to estimate the feasibility of constructing an implementation that matches specifications. In cases where you äre more likely to do well in estimating the utility of constructed implementations, the bottom-up approach works better. (Randell, 1968) In reality, though, a combination tends to be used, and development is an ongoing process of "I have this set of operations and I would like to construct a component that meets this specification -- how do I join those realities?" I agree strongly with your second point, though.
- kortilla 6y agoBut they aren’t the same at all. In one case you fully understand the implementation because you are the author. You’re just guessing what your consumers’ use cases will be. When developing a driver framework, you are the consumer and you’re just declaring what you want a provider to do. You don’t know anything about how it will be implemented nor do you care. They are completely different actors in the system designing the API with completely different goals. It’s not just top-down vs bottom-up unless you’re both the consumer and the implementor, which is just a trivial subset of API work.
- thecupisblue 6y agoI think it may be more accurate to describe it as >the process of expressing ideas that are conceptually familiar to users, often through metaphor. IMO fits it better than implementing interfaces.
- jesseduffield 6y agoI disagree about the primacy of naming and metaphor here. From my perspective, the only reason the unix pipe is intuitive to people is that it actually behaves like a pipe. I don't think that the abstraction of 'pipe' started by thinking of the metaphor, but rather it started by thinking about how best to reduce complexity when it comes to IO operations, and it just so happened that the best way to do it was with something resembling an actual pipe. That is to say, the first step is to find the representation of something which reduces overall complexity, and the next step is to think about naming. In my experience, one of the signs that you've found the right abstraction is that it's easy to find a name for it, because the intrinsic complexity of most things is low (or at least lower than I thought when I started programming), and so it's not hard to find metaphors when your abstraction brings the extrinsic complexity of a system down towards the level of intrinsic complexity of its requirements.