Are categories Design Patterns?
This is an episode of Thoughts on Functional Programming, a podcast by Eric Normand.
People often ask 'what are the design patterns of functional programming?' A common answer is that categories from category theory, like monads and functors, are the design patterns. But is that true? I explore the consequences of that answer.
Eric Normand: Are categories from category theory, design patterns? Hi, my name is Eric Normand. These are my thoughts on functional programming.
I often hear people ask, — these are object-oriented programmers — they are used to using design patterns just for context. They are used to learning the visitor pattern, the singleton pattern, the factory pattern, and they ask online what are the functional design patterns.
One common answer I see a lot is that categories, that is monads, monoids, functors. Those are design patterns that those are what functional programmers use when they are building their systems and their design patterns. That's always struck me the wrong way.
It just doesn't feel right. It's only been recently that I've figured out why it doesn't seem right to me. When I look at the design patterns, what I see is code patterns that are not reusable in the language.
In Java, you can't write a visitor abstract class or visitor interface, then just implement that, and now you have a visitor. It is a whole coding pattern that requires you, the programmer to have a lot of discipline implementing that pattern.
A lot of people would say that the design patterns are actually workarounds for limitations in the language. When you say functor is a functional design pattern, it doesn't carry that same connotation. You still with...in Haskell because the Haskellers are the ones that say functor is the design pattern.
Haskell are static type people. They will say, "Well, we have this thing called the functor type class and we implement it. Then we have a type that is a functor." It doesn't have that problem of it being a workaround for something that doesn't exist in the language.
That's why to me it doesn't seem like that's a design pattern. Why is that important? That idea that it doesn't have...The language doesn't have the feature that allows for that design pattern to be easily expressed because I feel what happens then is you don't have people looking for patterns that don't have features to support them.
If I were going to use functors in Clojure, or monads in Clojure, I would say that it's a design pattern. I don't like the word design pattern, but I'm going to work with the definitions and the terms that are out there. If you have a functor or a monad in Clojure because there isn't any support for that stuff in the language.
In Haskell, you need the type system to basically make them work right. Because of that, I would say that they are a design pattern in Clojure. If you want to do a monad, it's a lot of discipline from the programmer. You have to learn how to do the pattern. Then, you can code it up and someone else who looks at it, they have to learn this design pattern before they can understand it.
Where in Haskell, it's written right there. This implements monad in this way, so there you are done. It's not a thing that you have to learn separate as a pattern. The problem is then the Haskellers, as an example, when they use monads and call them a design pattern, they're missing other patterns. They're missing what their language doesn't support but that they do all the time.
Those are the things that actually need to get written down. Those are the tribal knowledge. Those are the things that a new programmer needs to be introduced to before they can understand what's happening. There's no syntax to look up. It's all discipline, and patterns and just coding practices.
If design patterns has any use at all, it's as that. It's as this thing that you can only recognize that it is part of the pattern because you understand the pattern. There's no piece of code that says, "This is the pattern," and then it's checked by something.
The same thing happens in Clojure, let's say, lazy evaluation. Lazy evaluation, you could say it's a pattern. In some languages, they don't have lazy evaluation, but then in Clojure, we do FOR sequences. If I come to Clojure and I call lazy evaluation a design pattern, FOR sequences, it's not true, like a lazy list. It's not a design pattern it's just a fact of the language.
In another context, another language, you actually have to implement that. It has a little bit of discipline, in how do you use it that thing.
I think that at least in terms of Haskell, we shouldn't say that the monads, the monoids and the applicative functors, they should not be design patterns because then it loses the meaning, the important meaning, that this is something that you need to learn that's not part of the language.
That's what we need to be looking for, to help people learn our language, to codify what we're doing. That's why the design patterns book was popular because people, they learn Java, they learn C++, they knew the language, but they're like, "I need more, I need to figure out how to do more with this language."
The design patterns gave them recipes and things that they could follow and learn to make them better programmers, to get them the power that they needed in their language. That's what we need to look for, in Clojure, in Haskell, in whatever language we're using, we need to be looking for those patterns that we use all the time that the language doesn't explicitly support.
In Clojure, I look at stuff like, using HashMaps to represent entities. There's nothing in the language that says you should do that, but it's very common that we do that. A beginner looks at it and it's like, "I don't know how to do this, and there's all these rules about it." We're going to use keywords for all of the attribute names. Then the values are going to be whatever type is appropriate for that.
What if we need to give it another bit of information about what kind of entity it is? That's another pattern there. You can also use HashMaps as indexes. It's an index where you get to look something up but the keys and the values are all the same type.
What's an example of an index? If I wanted to look up entities by key, I can make a HashMap where the ID is the key, and the value is the value. These are patterns that we use that the language doesn't have a name for, there's nothing in the language that says Entity or Index, but we do those all the time.
That's what we need to be looking for. That's what I always look for, what does the language not give us that people do all the time?
If you want to get in touch with me, I'm Eric Normand. On Twitter, I'm @ericnormand, E-R-I-C N-O-R-M-A-N-D. You can also email me, email@example.com. This has been fun, see you later.