Maybe you remember the following quote from classic The Fellowship of the Ring:
„What is that?“ – Pippin
„This, my friend, is a pint.“ – Merry
„It comes in pints? I’m getting one!“ – Pippin
Pippin’s surprise comes very close to my initial reaction after seeing for the first time just how much reuse there is in Clojure.
There’s two kinds of reuse I’d like to talk about:
- Reuse of constructs from the standard library
- Reuse of constructs from my other projects
Reuse of the standard library
In every of my projects in the past two years I have reused amazing amounts of functions from the standard library. The most recent example is the path-finding-algorithm from the coding dojo.
Ruse in functional languages generally is stronger than in OO languages. IMHO this is because side-effects / mutability don’t compose. Pure functions on the other hand are not dependent on outside state and thus easier to reuse.
Still Clojure’s approach to reuse is special. This is because of data orientation. Every Clojure construct works directly with data. This presses you to make everything you do also work directly with data. Because when you have done so you can reuse a lot of stuff.
Making everything work with data is effort, though. The question is: is it worth it? Regarding this I had a revelation recently.
Reuse of constructs from other projects
I’m working on a tool that lets me display arbitrary data in a swing UI. I made it to avoid this:
and get this instead:
Fast forward a few months.
During the coding dojo someone said: „we’ve written a pretty printer for mazes“. At first I thought this was a good idea, because manual tests are a lot easier that way. The following day however I realized I could use DSUI for that job. Matrices or nested vectors respectively are displayed as a table. And here is the result:
The 1s mark the path from the entrance (0, 0) to :Exit.
An interesting side-note is that my path-finding algorithm returns a list with all solutions. That list can be passed to DSUI directly and it creates a tabbed pane for every solution. This is even better than pretty printing (or print-table). It allows browsing all solutions, not just one.
To achieve this, all I had to do is pipe the output of my path-finding algorithm into DSUI. Done.
(dsui.core/dsui (solutions-at [[1 :W :_ :_ :_ :_] [:_ :W :_ :W :W :_] [:_ :W :_ :_ :_ :_] [:_ :_ :_ :W :Exit :W] [:_ :W :_ :_ :_ :_]] [0 0]))
This might seem like a poor example. And in a way it is. A tool for displaying arbitrary data allows displaying solutions for a labyrinth. Of course. The point I want to make here is: The reuse came without me anticipating it or designing for it. And that has never happened even once in my 15 years of developing Java. When I wanted to reuse constructs in my Java code, I had to design for it. Unplanned reuse did not happen.
So, is the effort of making everything work with data worth it? Oh heck yes, absolutely, a hundredfold! Once a tool interoperates with data, it interoperates with everything else in Clojure. Even in cases it wasn’t designed for. This example is only one of many I had the pleasure experiencing in the past years.
While Merry and Pippin’s conversation is about beer superficially, I strongly believe they were really making an inside joke about reuse. Reuse really comes in pints in Clojure.