Design patterns across paradigms by Mark Seemann
This blog post makes the banal observation that design patterns tend to be intimately coupled to the paradigm in which they belong.
It seems as though lately it has become hip to dismiss design patterns as a bit of a crutch to overcome limitations in Object-Oriented Design (OOD). One example originates from The Joy of Clojure:
"In this section, we'll attempt to dissuade you from viewing Clojure features as design patterns [...] and instead as an inherent nameless quality.
"[...]the patterns described [in Design Patterns] are aimed at patching deficiencies in popular object-oriented programming languages. This practical view of design patterns isn't directly relevant to Clojure, because in many ways the patterns are ever-present and are first-class citizens of the language itself." (page 303)
From what little I've understood about Clojure, that seems like a reasonable assertion.
Another example is a tweet by Neal Swinnerton:
"With functions that accept functions and/or return functions 400 of the 600 pages of design patterns can be burnt says @stilkov #euroclojure"
and Stefan Tilkov elaborates:
"@ploeh @sw1nn @ptrelford many OO DPs only exist to replace missing FP features in the first place"
First of all: I completely agree. In fact, I find these statements rather uncontroversial. The only reason I'm writing this is because I see this sentiment popping up more and more (but perhaps I'm just suffering from the Baader-Meinhof phenomenon).
Patterns for OOD #
Many 'traditional' design patterns describe how to solve problems which are complex when working with object-oriented languages. A lot of those problems are inherent in the object-oriented paradigm.
Other paradigms may provide inherent solutions to those problems. As an example, Functional Programming (FP) includes the central concept of Function Composition. Functions can be composed according to signature. In OOD terminology you can say that the signature of a function defines its type.
Most of the classic design patterns are based upon the idea of programming to an interface instead of a concrete class. In OOD, it's necessary to point this out as a piece of explicit advice because the default in OOD is to program against a concrete class.
That's not the case in FP because functions can be composed as long as their signatures are compatible. Loose coupling is, so to speak, baked into the paradigm.
Thus, it's true that many of the OOD patterns are irrelevant in an FP context. That doesn't mean that FP doesn't need patterns.
Patterns for FP #
So if FP (or Clojure, for that matter) inherently address many of the shortcomings of OOD that give rise to patterns, does it mean that design patterns are redundant in FP?
Hardly. This is reminiscent of the situation of a couple of years ago when Ruby on Rails developers were pointing fingers at everyone else because they had superior productivity, but now when they are being tasked with maintaining complex system, they are learning the hard way that Active Record is an anti-pattern. Duh.
FP has shortcomings as well, and patterns will emerge to address them. While FP has been around for a long time, it hasn't been as heavily used (and thus subjected to analysis) as OOD, so the patterns may not yet have been formulated yet, but if FP gains traction (and I believe it will), patterns will emerge. However, they will be different patterns.
Once we have an extensive body of patterns for FP, we'll be able to state the equivalent of the introductory assertion:
Most of the established FP patterns address shortcomings of FP. Using the FloopyDoopy paradigm makes most of them redundant.
What would be shortcomings of FP? I don't know them all, but here's a couple of suggestions:
Apart from calculation-intensive software, most software is actually all about mutating state: Take an order. Save to the database. Write a file. Send an email. Render a view. Print a document. Write to the console. Send a message...
In FP they've come up with this clever concept of monads to 'work around' the problem of mutating state. Yes, monads are very clever, but if they feel foreign in OOD it's because they're not required. Mutation is an inherent part of the OOD paradigm, and it very intuitively maps to the 'real world', where mutation happens all the time. Monads are cool, but not particularly intuitive.
FP practitioners may not realize this, but a monad is a design pattern invented to address a shortcoming in 'pure' functional languages.
As Phil Trelford kindly pointed out at GOTO Copenhagen 2012, OOD is often characterized by 'dot-driven development.' What does that mean?
It means that given a variable, we can often just enter ".", and our IDE is going to give us a list of methods we can call on the object:
Since behavior is contained by each type, we can use patterns such as Fluent Interface to make it easy to learn a new API. While we can laugh at the term 'dot-driven development' it's hard to deny that it makes it very easy to learn.
The API itself carries the information about how to use it, and what is possible. That's very Clean Code. Out-of-band documentation isn't required.
I wouldn't even know how to address this shortcoming in FP, but I'm sure patterns will evolve.
Different paradigms require different patterns #
All of this boils down to a totally banal observation:
Most patterns address shortcomings specific to a paradigm
Saying that most of the OOD patterns are redundant in FP is like saying that you don't need oven mittens to pour a glass of water.
There might be a slight overlap, but I'd expect most patterns to be tightly coupled to the paradigm in which they were originally stated.
There might be a few patterns that are generally applicable.
The bottom line is that FP isn't inherently better just because many of the OOD design patterns are redundant. Neither is OOD inherently better. They are different. That's all.
Reg. mutatable state:
"Mutation is an inherent part of the OOD paradigm, and it very intuitively maps to the 'real world', where mutation happens all the time."
As Rich Hickley also told us at Goto Copenhagen, above statement may not be entirely true. Current state changes, but that does not mean that previous state ceases to exist. Also, as software devs we are not necessarily interested in representing the real world, but rather a view of the world that suits our business domain. So the question becomes: does the OOD notion of mutable state suit our business domain, or is the FP model, where everything is an immutable value in time, a better fit?
While we are programmers, we are still human, and unless you get involved in pretty advanced theoretical physics, a practical and very precise world view is that objects change state over time.
Alson, I see dot-driven development even more production in functional programming, or at least what I do as FP in C#. Function composition is nicely enhanced using static extensions over Func, Action and Task. I used this in one of my posts on comparing performance of object instantiation methods.
discussed the necessity of Pattern in FP.
The notion that pattern are not required in FP seems to be around for some time,
PaulGraham said "Peter Norvig found that 16 of the 23 patterns in Design Patterns were 'invisible or simpler' in Lisp." http://www.norvig.com/design-patterns/
- Trying to combine OOD and pure FP is pointless and misses the point. Less is more.
- Design patterns are analogous to cooking recipes.
- Monads are not a "design pattern invented to address a shortcoming in 'pure' functional languages".
I came late to comment, but again I'm here due to an exchange of tweets with the author. I've pointed out a S.O. question titled SOLID for functional programming with a totally misleading answer (in my opinion, it's clear).
In that case the answer lacks technical correctness, but in this post Mark Seemann points out a concept that is difficult to contradict.
Patterns are abstraction that provides concrete solutions (pardon the pun) to identifiable problems that present themselves in different shapes.
Tha fact that when we talk of patterns we talk of them in relation to OO languages, it just a contingency.
I've to completely agree with the author; functional languages need patterns too and as more these become popular as more functional patterns will arise.