
Clojure Design Patterns - simonpure
http://mishadoff.com/blog/clojure-design-patterns/
======
_diyu
Clojure is fantastic for its simplicity, and I worked on a Clojure code base
full time for 5 years, coming up with ways of translating Java patterns into
Clojure. But I've noticed it has not been gaining traction in the job market,
and now I feel like that 5 years has been a waste of valuable career building
time, which I wish I had spent gaining more experience in ( _sigh_ ) C# and
.NET, so that I could get the more local jobs which all seem to use the
Windows ecosystem, so we don't have to uproot our family and move to the city.
Not that I don't know C# and .NET, but I definitely don't have 3+ years
experience in it and I definitely don't have any experience MS SQL. So being
able to do really cool things in Clojure, and especially having excellent
rapid development skills with REPL-driven development, has done me no good.
The one remote job that I found that uses Clojure turned me down because they
used zippers in the code test, and that's literally the one Clojure construct
I just can't wrap my head around, which is ironic because idiomatic Clojure
really doesn't use recursion in the first place, favoring streams and
transducers instead. My career future doesn't really seem to have a lot of
Clojure in it unfortunately. Plus most of modern JavaScript has most of the
same benefits as Clojure anyway, especially in terms of FP.

~~~
modernerd
As Evan Czaplicki (creator of Elm) said:

“If functional programming is so great, why is it still niche? We have a
product that can practically eliminate runtime errors, make refactoring much
easier, lighten the testing burden, all while being quite delightful to use.
What’s the hold up?”

\- [http://www.elmbark.com/2016/03/16/mainstream-elm-user-
focuse...](http://www.elmbark.com/2016/03/16/mainstream-elm-user-focused-
design)

I'm just getting into Clojure and enjoy working with it so much that I don't
care if there are job opportunities at the end of it to justify the time
investment and mind-reboot. It's the first language that I didn't feel like I
was fighting, and Parinfer has made all those “weird parentheses“ a joy:
[https://shaunlebron.github.io/parinfer/](https://shaunlebron.github.io/parinfer/)

That said, there do seem to be more opportunities than last time I looked at
Clojure/Scala/Haskell (Functional Works, mentions in HN Who's Hiring) – it
feels like people are waking up to FP ideas after React stormed the front-end
scene.

~~~
noncoml
> “If functional programming is so great, why is it still niche? We have a
> product that can practically eliminate runtime errors, make refactoring much
> easier, lighten the testing burden, all while being quite delightful to use.
> What’s the hold up?”

I think they are over-estimating how much people care about runtime errors.

~~~
hellofunk
I agree. I've worked in both Elm and ClojureScript. In one, I have to
constantly deal with compiler errors. In the other, about as much runtime
errors. The net time for error handling is the same. But the net time in
actually writing code is in favor of ClojureScript, which requires at least
half as many lines as Elm (and often much less than half).

Error handling is somewhat over-emphasized in comparison to other things that
affect productivity (and maintainability).

------
thom
I hate these deliberately obtuse articles about design patterns every time
they bubble up each year. Patterns are about naming and communicating design
choices. Just shouting 'function' over and over again isn't really relevant.

Like, it's true you can implement a Strategy with a function. And yet the
majority of libraries on my current Clojure project don't let you supply a
function as a strategy even when it'd make sense. Because none of the authors
have made that design decision. Also your commands don't support undo. That'd
take _two_ functions. And you know how you might communicate what those _two_
functions taken together do? You could use patterns! So somehow, just having a
language feature doesn't MAGICALLY lead one to make one design decision over
another. It didn't in the 90s, it didn't in the 00s, and it doesn't now.

Looking forward to 2025's version of this snarky article in Julia or whatever
though.

~~~
blain_the_train
Your right, having a to tool doesn't mean you use it correctly. However, the
harder it is to apply a tool the less likely you are to do so. I think the
authors point isn't to demonstrate that the design is bad, but rather how it's
done in clojure.

~~~
thom
C++ or Java or whatever not matching someone's aesthetics doesn't change the
fact that providing an interface that accepts a Strategy or works with
Commands is a design choice, not a language feature. It doesn't matter how
it's implemented. The snippets of code in the GoF book aren't the point of
design patterns, they're in no way canonical, and people pretending for
_decades_ that they are really grinds my gears.

But not as much as Clojure having no good implementation of the age old
'Readable Stacktrace' design pattern. :)

~~~
sooheon
> It doesn't matter how it's implemented

Well it does, a little bit. If you implement them by aligning rocks on a sunny
beach, it might take a tad longer and more prone to bit rot. Java may as well
be that for some people.

------
hellofunk
This article might appeal to some out of curiousity but it is not of much
practical value. Design patterns are not a thing in Clojure. The whole idea
behind the flexibility of the language is that you don't need a pre-existing
pattern to force on your problem; instead, the language makes it possible for
each problem to build its own pattern. (And that is the point, I think, in
this article.)

I've worked with and around Clojure developers for years now, and no one talks
about traditional design patterns. Design patterns add complexity, not
simplicity, to a language. By contrast, Clojure solutions are relatively
simple. Design patterns often evolve in languages where you struggle against
the language's design to express a solution. When a language strips away those
obstacles, pre-existing patterns become less necessary.

------
ellyagg
"Design patterns are bug reports against your programming language."

– Peter Norvig

[http://norvig.com/design-patterns/design-
patterns.pdf](http://norvig.com/design-patterns/design-patterns.pdf)

~~~
sidlls
There is a programming language which captures all patterns in its spec?
Fascinating.

Alternatively, even if one agreed with the quote, it isn't very useful: it is
simply another way to state that no programming language does everything.

~~~
tikhonj
No, the idea is that your language should be able to express patterns _in the
language_. Then they're not patterns any more—they're libraries! Working with
an explicit abstraction from a library is far more pleasant than working with
an implicit abstraction from a book.

The way to achieve this is emphatically _not_ adding patterns to the language
spec. Instead, you want small and simple languages that can _grow_ —languages
flexible enough to add new abstractions that let you write code the way you
want.

"Growing a Language"[1] by Guy Steele expounds on this and, as a bonus, is the
single best technical talk I've ever watched. Highly recommend watching it[1]
or, if you're pressed for time, reading the transcript[2].

[1]:
[https://www.youtube.com/watch?v=_ahvzDzKdB0](https://www.youtube.com/watch?v=_ahvzDzKdB0)

[2]:
[https://www.cs.virginia.edu/~evans/cs655/readings/steele.pdf](https://www.cs.virginia.edu/~evans/cs655/readings/steele.pdf)

------
fluff-brain
Clojure / ClojureScript _is_ fantastic for its simplicity.

It takes a while to overcome education and job conditioning and take hold. At
first, you're like, ha ha, this is not real. Then, you're like, well, this
isn't real but it can inspire some changes in the way I program. Then, you
notice yourself doing stupid busy-work in Java or Perl and think, hmm. Hmm.
Then, it's like, ya know, this entire project I'm working on would have been
sooo much simpler in Clojure. Then, you're like, screw it, I'll just do this
minor JDBC data migration or little process-simulation webpage in Clojure.
whee! And finally: there is just no other way. Bring it on! Any task, it comes
out better and sooner and much, much smaller in Clojure.

But those stages may take years.

------
marvel_boy
Awesome. All "Gang of four" patterns are just functions !

~~~
jeremiep
Wasn't that the joke when the book was first released? All design patterns
merely handle the lack of anonymous and first-class functions.

~~~
ssfak
..and closures

~~~
virgilp
... and pattern-matching. But yeh, patterns are a sign of a weakness in the
language. And it's not infrequent that patterns really turn out to be anti-
patterns.

~~~
geezerjay
> But yeh, patterns are a sign of a weakness in the language.

That line of reasoning never ceases to be amusing by how silly and self-
defeating,particularly as those who make those claims try to pin ignorance on
others while manifesting their own ignorance. They seem to be entirely
oblivious to the fact that the main goal and usefulness of patterns is to
communicate intent and provide semantics and use a common language to describe
common problems. A function is a function but a strategy pattern isn't a state
pattern. Those who miss this fundamental difference are totally oblivious to
what design patterns actually are.

~~~
blain_the_train
Right, a function isn't a pattern. But a pattern in the common usage is some
functionality that needs to be communicated outside the core language. In
languages that offer that functionaliry in the core, I would argue there no
longer "patterns". I don't think many of the gof patterns are remarkable in a
modern Language like clojure, because the options are obvious.

~~~
mirekrusin
It should be clarified that the whole thing is about functional patterns in
Clojure that are expressing same functionality as well known OO patterns in
Java.

Adding "it's just..." in front of functional patterns doesn't make them non-
patterns.

Patterns are named ideas on how to approach/solve similar problems.

They are not tied to OO programming, Java, they are not good or bad per-se;
it's just a cookbook with recipes that have names.

Otherwise nice format, I like it.

~~~
catnaroek
Presumably the point is that most functional programmers wouldn't have
bothered giving dedicated names to, say, “strategies” or “factories”, more or
less the same way nobody would bother calling “(a + b) / 2” the “average
pattern”.

~~~
geezerjay
> nobody would bother calling “(a + b) / 2” the “average pattern”.

don't you mean.... the arithmetic mean?

[https://en.wikipedia.org/wiki/Arithmetic_mean](https://en.wikipedia.org/wiki/Arithmetic_mean)

Oh my god, and people bother themselves calling the addition o sequences of
numbers as summation.

[https://en.wikipedia.org/wiki/Summation](https://en.wikipedia.org/wiki/Summation)

Where does this madness end? These morons even bothered themselves forming
whole fields in mathematics by studying adding numbers

[https://en.wikipedia.org/wiki/Series_(mathematics)](https://en.wikipedia.org/wiki/Series_\(mathematics\))

Why is humanity so wrong about such a simple concept? Why is everyone bothered
to give dedicated names to concepts you fail to understand?

~~~
catnaroek
The mean is an precisely defined operation in mathematics, not a vaguely
defined “pattern” in software engineering.

I insist: You only need “patterns” when you can't articulate your ideas
precisely in a formal language, such as a programming language or mathematics.

------
truth_seeker
Comapred to any tech talk videos, This article convinced me to try Clojure
almost 2 years back. Very well written and practical.

------
catnaroek
This is a restatement of the well-known equivalence between so-called
“objects” and “functions”. Both are idiosyncratic names for the same thing:
procedural values.

The condescending tone of the post is completely unwarranted, especially since
it glosses over the real limitations of procedural values as abstraction
building blocks, which don't magically go away when you rename them from
“objects” to “functions”. In no particular order:

(0) There is no modular way to distinguish procedures that meet a nontrivial
specification from all other procedures. Guaranteeing that a procedural
argument never takes a silly value requires whole-program analysis, which
doesn't scale.

(1) There is no modular way to communicate the fact that two or more
procedures manipulate similar or related data structures. Try implementing
abstract data types with binary operations (e.g. mergeable heaps) as objects.

(2) There is no modular way to control which procedures exchange information
with one another. Say Foo is designed to communicate with Bar. I can replace
Bar with a type-compatible but ill-behaved impostor Qux. As far as Foo can
tell:
[https://az616578.vo.msecnd.net/files/2016/09/26/636105299923...](https://az616578.vo.msecnd.net/files/2016/09/26/636105299923578088-1610436935_its%20fine.jpg)

The cure for this illness is using abstract data types, not objects.

~~~
sooheon
Can you enlighten me as to how ADTs differ from specified contracts like spec
[1]?

[1]: [https://clojure.org/about/spec](https://clojure.org/about/spec)

~~~
catnaroek
Abstract data types guarantee that nobody besides the implementor can violate
the contract. They compartmentalize what you have to verify.

Without abstraction enforcement mechanisms, an abstraction you have correctly
implemented can still misbehave, because someone else has tampered with its
internal representation.

~~~
hellofunk
And here I thought ADT almost always meant "algebraic data type."

Regardless, Rich Hickey's last talk (last month at Clojure Conj) offered a
counter argument, why ADTs are usually not the best way to organize software
(as part of his larger discussion against static typing).

~~~
catnaroek
> And here I thought ADT almost always meant "algebraic data type."

It depends on context

> why ADTs are usually not the best way to organize software

He clearly hasn't proven anything about his code.

~~~
mike706574
> He clearly hasn't proven anything about his code.

Is that a bad thing? It seems like many people are able to write software that
works well and gets the job done without "proving anything" about their code
in the way you're describing. Personally, I'm fine with not proving anything
if I can deliver quickly and everything works, but I'd like to be convinced
otherwise if there's real value there.

~~~
catnaroek
For basic infrastructure such as compilers and standard libraries (at least of
data structures and algorithms), not providing proofs of correctness is
outright criminal.

~~~
blain_the_train
It's not though is it... You know, because there aren't actual laws about it.

Let's try not to make inflammatory statements.

------
martin1975
I'd be curious to see if the Scala version would be just as terse as the
Clojure one, of course following the FP paradigm as well as possible .. any
resident Scala gurus?

~~~
culturedsystems
There's a master's thesis by Fredrik Skeel Lokke which is an extensive
discussion of GoF design patterns in Scala: [https://www.scala-
lang.org/old/sites/default/files/FrederikT...](https://www.scala-
lang.org/old/sites/default/files/FrederikThesis.pdf)

------
based2
combinatations ?

