1. 11
  1.  

  2. 19

    I take a very firm and mathematically oriented stance on the matter of abstraction.

    Mathematicians understand this very well, but programmers do not understand the difference between an abstraction and a wrapper and a generalization.

    A set is a very weak mathematical object by itself, the power of set theory comes from the set axioms. It’s the axioms where the abstraction lies, and not in the sets.

    A programmer might think that ℤ is an abstraction over ..., -1, 0, 1, ..., but the true abstraction is the fact that the ring (ℤ, +, *) exists. It’s the abstract notion of a ring where the power of abstraction lies.

    Abstraction is all about semantics, and the semantics are abstract.

    You can always wrap something in something else: S = {(x, 1) | x ∈ ℝ}.

    You can always generalize anything: S = ℚ⁺ ⨯ ℤ⁻.

    These, an abstraction do not make. If you can’t define the abstraction without invoking the elements you are abstracting over, it’s not an abstraction.

    The idea of an abstraction leak is a misnomer. Abstraction can’t leak by definition, there’s nothing to leak. What’s not in the abstraction is not in the abstraction. What we call abstraction leaks happen because of insufficient or wrong abstractions. Abstraction leaks is something that happens because of the lack of abstraction. It’s not a problem with the abstraction.

    You can have “abstraction leaks” in mathematics too, but the interpretation is completely different. A programmer might think that Riemann integrals are leaky because they can’t integrate every function. A mathematician knows that Riemann integrals simply don’t have enough abstractive power, and will choose other kinds of abstractions to do his or her integrals like Lebesgue integrals.

    Of course the utility of calculus comes from the linear structures it generates, not from the act of calculating integrals. THAT is where the abstraction trully lies.

    Operating systems and programming languages are in the business of providing abstractions. Libraries in most languages can’t create new abstractions.

    Features are not abstractions. There is a trend in modern operating systems to provide features without providing abstraction. For example, eBPF exists outside the process abstraction. The set of processes plus eBPF do not have any kind of operative abstraction, they are simply a generalization.

    Why do I care about all of this? Because abstraction are powerful, and because generalization or wrapping without abstraction is taking power away.

    1. 13

      It’s easy to ignore or dismiss lots of things that Dijkstra said (and sometimes we should), but his quote seems appropriate for agreeing with @4ad.

      The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise

      1. 2

        To take your example and run with it, the fact that the ring of integers exists is visible from multiple perspectives in programming. Each of these perspectives corresponds to a way of maintaining and operating code. Note that, in all cases, we are trying to instantiate the abstraction of the ring onto an existing abstraction of integers, and so we are really highlighting the interface between abstractions rather than the abstractions themselves.

        • We can use property tests to spot-check elements of the abstraction. I did this in Monte.
        • We can use type-driven proofs to define the data of the abstraction alongside a proven type signature. This is done in Agda.
        • We can use syntactic definitions to assert the existence of the abstraction as a logical entailment. This is done in Metamath.
      2. 3

        This is an old brain-dump, but I’m posting it in response to https://lobste.rs/s/mxcmxg/abstraction_is_okay_magic_is_not. I should definitely update this, and add whatever magic is. I don’t think “magic” is precisely the same as either of my three categories.

        1. 1

          It’s a good brain-dump, and I agree with it. I will note that my post (I wrote the ‘Abstraction is Okay’ article) was not exactly aimed at defining what abstraction is. Instead it’s just a musing on a pattern I’ve experienced as a consumer many times and how I consciously avoid producing it myself.

          I have concrete examples of “magic,” but I’d rather not share them because they’d tend to be very recognizable to the people who wrote them. Here’s an anonymized example I debated including in the post:

          @Wire(WidgetModel)
          @NormalPaths(exclude = AsyncPaths)
          @VarySerializer(determinant = ClientAuthentication)
          class WidgetService extends BasicModelService {
              // No code necessary!
          }
          

          What does this do? What does it accept? How do you modify it? It’s the kind of thing that someone presents and says proudly: “Look at how easy it is to implement a service!” but its name quickly becomes a portent of dread the minute they move on to another project or another job.

          “Hey, Beth, I was wondering how this thing in MagicalWeb works…”

          “Oh, man. Don’t worry about that crap, that’s Chuck’s stuff. He’s been gone for ages, and we’re trying to kill it. Add your path to the if statement in SkipThatCrap.java.”

        2. 3

          I think abstraction actually means like six different things and a lot of discussions about abstraction are because people are conflating some of these things. Kinda like “naming things”.

          EDIT: literally the exact thing this article is about, I didn’t read it well enough

          1. 3

            I’m not remotely surprised you have identified more buckets than I have. Can I ask what they are?

            1. 2

              I haven’t actually identified any buckets! It’s just the way the discussion “tastes” to me, you know? Like reading arguments about it fall into that kind of template where it seems everybody is thinking about something different when they use abstraction. I’ll think on this more and get back to you with some possible differences.

              EDIT: oh my god, this is basically the exact same point you made in the article. I really shouldn’t be trying to write hot takes while sleep deprived. Sorry.

          2. 2

            I absolutely loathe the fact that deduplication is considered abstraction by, seemingly, the majority of programmers.

            I understand that technically, if you factor out duplicate code and name it something, that it can be seen as abstraction if you squint hard, but it’s not really abstraction in any meaningful sense. Abstraction is about creating a new level on some kind of conceptual hierarchy. Just eliminating copy and paste doesn’t (always) allow you to reason on a new level- it just let’s you type less.

            I tried to make a similar point in a HackerNews thread and the only example I could come up with was a very mediocre example about arithmetic:

            Let’s say you have several functions that do some arithmetic. Maybe you notice in one function you write x * x * x and in another you wrote y * y * y. You might write a new function called multiply_self_three_times(x) = { x * x * x }. Did you really abstract anything? I’d say no. However, if you discovered/invented exponentiation and wrote exp(x, n) = {TODO()}, then you’ve abstracted something!

            1. 1

              One pithy way of putting this bit of folklore is that lambda-abstractions are both universal and also the weakest sort of abstraction. Any abstraction can be implemented with lambdas, but the presence of a lambda does not denote a “conceptual hierarchy” or other structure.