Some design patterns as universal abstractions by Mark Seemann
Some design patterns can be formalised by fundamental abstractions.
This article series submits results based on the work presented in an even larger series of articles about the relationship between design patterns and category theory.
Wouldn't it be wonderful if you could assemble software from predefined building blocks? This idea is old, and has been the driving force behind object-oriented programming (OOP). In Douglas Coupland's 1995 novel Microserfs, the characters attempt to reach that goal through a project called Oop!. Lego bricks play a role as a metaphor as well.
Decades later, it doesn't look like we're much nearer that goal than before, but I believe that we'd made at least two (rectifiable) mistakes along the way:
Over the years, I've seen several attempts at reducing software development to a matter of putting things together. These attempts have invariably failed.
I believe that one of the reasons for failure is that such projects tend to aim at coarse-grained building blocks. As I explain in the Interface Segregation Principle module of my Encapsulation and SOLID Pluralsight course, granularity is a crucial determinant for your ability to create. The coarser-grained the building blocks, the harder it is to create something useful.
Most attempts at software-as-building-blocks have used big, specialised building blocks aimed at non-technical users ("Look! Create an entire web site without writing a single line of code!"). That's just like Duplo. You can create exactly what the blocks were designed for, but as soon as you try to create something new and original, you can't.
OOP is another attempt at software-as-building-blocks. In .NET (the OOP framework with which I'm most familiar) the Base Class Library (BCL) is enormous. Many of the reusable objects in the BCL are fine-grained, so at least it's often possible to put them together to create something useful. The problem with an object-oriented library like the .NET BCL, however, is that all the objects are special.
The vision was always that software 'components' would be able to 'click' together, just like Lego bricks. The BCL isn't like that. Typically, objects have nothing in common apart from the useless
System.Object base class. There's no system. In order to learn how the BCL works, you have to learn the ins and outs of every single class.
Better know a framework.
It doesn't help that OOP was never formally defined. Every time you see or hear a discussion about what 'real' object-orientation is, you can be sure that sooner or later, someone will say: "...but that's not what Alan Kay had in mind."
What Alan Kay had in mind is still unclear to me, but it seems safe to say that it wasn't what we have now (C++, Java, C#).
Building blocks from category theory #
While we (me included) have been on an a thirty-odd year long detour around object-orientation, I don't think all is lost. I still believe that a Lego-brick-like system exists for software development, but I think that it's a system that we have to discover instead of invent.
As I already covered in the introductory article, category theory does, in fact, discuss 'objects'. It's not the same type of object that you know from C# or Java, but some of them do consist of data and behaviour - monoids, for example, or functors. Such object are more like types than objects in the OOP sense.
Another, more crucial, difference to object-oriented programming is that these objects are lawful. An object is only a monoid if it obeys the monoid laws. An object is only a functor if it obeys the functor laws.
Such objects are still fine-grained building blocks, but they fit into a system. You don't have to learn tens of thousands of specific objects in order to get to know a framework. You need to understand the system. You need to understand monoids, functors, applicatives, and a few other universal abstractions (yes: monads too).
Many of these universal abstractions were almost discovered by the Gang of Four twenty years ago, but they weren't quite in place then. Much of that has to do with the fact that functional programming didn't seem like a realistic alternative back then, because of hardware limitations. This has all changed to the better.
Specific patterns #
In the introductory article about the relationship between design patterns and category theory, you learned that some design patterns significantly overlap concepts from category theory. In this article series, we'll explore the relationships between some of the classic patterns and category theory. I'm not sure that all the patterns from Design Patterns can be reinterpreted as universal abstractions, but the following subset seems promising:
- Composite as a monoid
- Null Object as identity
- Builder as a monoid
- Visitor as a sum type
- Chain of Responsibility as catamorphisms
- The State pattern and the State monad
- Postel's law as a profunctor
- The Liskov Substitution Principle as a profunctor
- Backwards compatibility as a profunctor
Some design patterns closely resemble categorical objects. This article provides an overview, whereas the next articles in the series will dive into specifics.
Next: Composite as a monoid.