Please read the latest chapter in Runnable Specifications, Composition Lens Part 2. It’s all about algebraic properties. You may have read some of it before in Part 1, but that was also significantly reworked. I’d love to know how this chapter is landing. Where are you getting confused? What is surprising? What is powerful?
I can still remember the feeling when I started to differentiate the implementation from the specification. It was some time in University. We were coding quite a lot, and I could see how multiple implementations were possible for any given specification. But I could also see that the specification itself had many choices and so it, too, could be designed.
Some people consider the ability to distinguish between implementation and specification as the defining feature of senior programmers. Junior programmers can make things work, but until they can think in terms of clear requirements for the implementation separate from the code that meets those requirements, they can’t become senior. The requirements must incorporate multiple perspectives, including functionality, business needs, maintenance costs, etc.
Last week, I reversed the arrows of programming from an act of concretizing to an act of abstraction. You’ll remember that abstraction is a mapping from a concrete domain to an abstract domain. We typically think of programming as taking our abstract ideas and mapping them to concrete machine instructions to command a computer. The reversal is to think of the computer’s machine code as an abstract domain. In that case, the act of programming is finding a mapping from our domain concepts to the abstract formal system called machine code.
I believe that the shift to programming as abstraction is as profound as separating specification from implementation. This mindset can lead to new approaches to programming and can form groundbreaking products.
In this issue, I’d like to explore this idea some more. If you’d like to explore with me, please be aware that these ideas are at the furthest reaches of my understanding. A lot of the ideas are undigested. Obvious connections have not been made. Still, I’ve gathered enough raw stuff for interested people to find something of value.
I developed this mindset somewhere over the last ten years. I’ve called it “algebraic thinking” before, but I think the idea of reversing the arrows captures more of the essence of it. I want to explore two things. One is the set of new modes of thinking that this mindset opens up. The other is references to other work that I believe embodies this mindset.
New modes of thinking
The reversal of the arrows allows for several new modes of thinking.
When you reverse the arrows, design and implementation are both done in the programming language, not separately. The programming language is the medium of specification, because it is formal enough to express the desired ideas. The danger is that it is too easy to cross the line into implementation. Traditional modeling approaches, like UML, tend to design outside of the programming language. The code is for implementation.
When you reverse the arrows, static analysis like partial evaluation (such as static type checking) is not simply a way to check for errors, but yet another abstraction (mapping from expressions to their types) that can help you explore a space. In other words, types help you design directly in code.
But this applies to other forms of analysis, such as automated testing. Tests can be devised to ensure that our code maintains algebraic properties. This is the basis of property-based tests. We typically hear people explaining property-based testing as “automatically create thousands of test cases.” But this misses the more important idea that your tests can assure that your mapping behaves as expected.
Finally, when you reverse the arrows, you begin to think of the meaning of your expressions rather than the behavior of your code. You are looking to construct semantics from other semantics. Put another way, you want to test that f(a, b)
means the same thing as f(b, a)
, rather than that they behave the same way.
References to other work
This section will be a little bit of a hodgepodge. But it is what it is.
Alan Kay and the Smalltalk team were definitely in this mindset. I believe the generativity of that group was due in part to their reversed-arrow mindset. Check out this quote:
My math background made me realize that each object could have several algebras associated with it, and there could be families of these, and that these would be very very useful.
There must be some connection with the Curry-Howard Isomorphism, which states that types are to programs as logical statements are to proofs. I’m not informed enough to say very much, but I believe the connection between logic and programming give us a formal enough system to map domain concepts to.
Conal Elliott has spoken and written extensively about what he calls Denotational Design and Type Class Morphisms. The basic idea is that you design in code (typically Haskell) and map your domain concepts onto category theory type classes like functors. His brand of thinking has resulted in interesting ideas like Functional Reactive Programming. And his re-working of deep learning using automatic differentiation definitely stems from this kind of thinking.
Over the last 10 years, I’m clumsily tried to express the transformation in thinking I was going through. Building Composable Abstractions. Clojure is Imperative. Algebraic thinking. These were attempts at touching on this mindset.
Conclusions
I believe the mindset that the reversal of the arrows represents is a level of thinking that comes with many years of experience. We can see many high-level programmers adopting it and using it to develop new modes of programming.
Unfortunately, I think it appears alienated from practical concerns, unlike separating implementation from specification. That distinction at least helps engineer better products. I suspect this mindset is important in building platforms (like MapReduce at Google and Rama from Red Planet Labs), which are incredibly practical but hard to justify working into the next sprint.
And I also think it’s possible that it is opposed to another mindset that develops with time, which is to incorporate the business strategy and vision into the engineering thinking, what often gets called Staff Engineer. Are these two opposed? Or are they two sides of the same mindset?