Jay Fields is a software developer at DRW Trading. He has a passion for discovering and maturing innovative solutions. His most recent work has been in the Domain Specific Language space where he's delivered applications that empowered subject matter experts to write the business rules of the applications. He is also very interested in maturing software design through software testing. Jay is a DZone MVB and is not an employee of DZone and has posted 116 posts at DZone. You can read more from them at their website. View Full User Profile

Clojure: Composing Functions

  • submit to reddit

Before Clojure, I had never used a Functional Programming language. My experience has primarily been in C#, Ruby, & Java. So, learning how to use Clojure effectively has been a fun and eye-opening experience.

I've noticed a few things:

  • The Clojure language has a lot of built in support for maps and hashes (literal syntax, destructuring, etc)
  • Rich Hickey is quoted as saying "It is better to have 100 functions operate on one data abstraction than 10 functions on 10 data structures." Clojure reflects this opinion and has loads of functions that work with Sequences.
  • I strongly dislike working with people who think Map<String, Map<String, String>> is generally a good idea in Java. However, all the support for maps and vectors in Clojure makes working with them easy and efficient.
  • Once you embrace functions, maps, vectors, etc, composing functions together to transform data becomes effortless.
Like I said, I'm still learning Clojure. Recently I refactored some code in a way that I thought was worth documenting for other people who are also learning Clojure.

Let's start with a simple example. Let's assume we want to track the score in the British Open (Golf). The data structure we will be working with will be a map of maps, keyed by country and then player.
{:England {"Paul Casey" -1}}
The above example shows that Paul Casey plays for England and currently has the score of -1. We need a function that allows you to update a players score. Let's also assume that scores for individual holes come in the following map form.
{:player "Paul Casey" :country :England :score -1}
Given the above code, you would expect the following test to pass.
(def current-score {:England {"Paul Casey" -1}})

(deftest update-score-test
  (is (= 
    {:England {"Paul Casey" -2}} 
    (update-score current-score {:player "Paul Casey" :country :England :score -1}))))
(Let's ignore the fact that I'm def'ing current-score. It's only for simplicity's sake.)

There are a lot of ways to make that test pass. It took me a few tries to come up with something I liked. Below is the path I took.

Step one, get the test passing using the functions I know.
(defn update-score [current update]
  (let [player (:player update)
        country (:country update)
        hole-score (:score update)]
    (merge current {country {player (+ ((current country) player) hole-score)}})))
The above example code is a victory when you are learning Clojure. The tests pass, you've used a few features of the language. It's a great first pass. However, I'm sure the Clojure veterans are drooling over the refactoring possibilities.

Step two, learn the update-in function. Like I previously mentioned, Clojure has a lot of functions for working with sequences (maps, vectors, lists, etc). It's not always obvious which function you should be using. However, I've had loads of success learning from the mailing list, and getting immediate answers in the #clojure chat room on freenode. One way or another, I eventually found the update-in function, which is designed up update a nested key.
(defn update-score [current update]
  (let [player (:player update)
        country (:country update)
        hole-score (:score update)]
    (update-in current [country player] (fn [_] (+ ((current country) player) hole-score)))))
The switch to update-in is relatively easy and cleans up a bit of code, but now I've introduced an anonymous function. Anonymous functions are great, but if I can get away with not creating them, I always take that route.

However, before we eliminate the anonymous function, we're actually better off making sure we understand the new function (update-in). While the current version of the code works, it's much cleaner if you use the current value that is passed into the anonymous function instead of looking up the value yourself.
(defn update-score [current update]
  (update-in current [(:country update) (:player update)] (fn [overall-score] (+ overall-score (:score update)))))
Since we no longer use country and player in multiple places, it seems like removing the let statement is a logical choice. As a result, the amount of code necessary to solve this problem seems to get smaller and smaller.

However, we can actually take this a step farther and use destructuring to provide an even cleaner implementation.
(defn update-score [current {:keys [country player score]}]
  (update-in current [country player] (fn [overall-score] (+ overall-score score))))
If you aren't familiar with destructuring the syntax might be a bit confusing. There's a decent write up with destructuring examples available on clojure.org. I would also recommend writing a few simple functions and try out the examples for yourself.

There's one more step to take, but for me it was a big one. The documentation for update-in states:
Usage: (update-in m [k & ks] f & args)

'Updates' a value in a nested associative structure, where ks is a sequence of keys and f is a function that will take the old value and any supplied args and return the new value, and returns a new nested structure.
What I currently have works fine, but the additional args that update-in takes allows me to provide a more concise solution that I greatly prefer.
(defn update-score [current {:keys [country player score]}]
  (update-in current [country player] + score))
This final version doesn't require any anonymous functions. Instead it relies on on update-in to apply the current value and any additional args to the function that you passed in. The final version is so concise you begin to wonder if you really need to define an update-score function.

The final snippet is an example of why I'm very intrigued by Clojure these days. The ability to compose functions and mix them in with the powerful functions of the language results in very concise code.

I consider what I would need to write to accomplish the same thing in Java or Ruby, and I have to wonder if the Functional Programming supporters might just be on to something.

From http://blog.jayfields.com/2010/07/clojure-composing-functions.html

Published at DZone with permission of Jay Fields, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)



Marc Stock replied on Wed, 2010/07/21 - 11:13am

Very nice writeup. This is what it's all about and unfortunately, this is what is completely lacking in all the "learning Clojure" materials that I've seen. Syntax is pretty easy to learn (even for a lisp) but function composition is an art that's completely glossed over. In fact, it's what caused me to drop my pursuit of learning Clojure (and I even went to the Pragmatic class that's taught by Hickey and Halloway).

Dave Newton replied on Thu, 2010/07/22 - 9:07am in response to: Marc Stock

"Syntax is pretty easy to learn (even for a Lisp)..."? It's nearly syntax-free (and Clojure has some syntax other Lisps don't)--it had *better* be easy to learn the syntax. It's too bad you gave up on Clojure because you don't get function composition yet--I'd recommend spending some more time with it, whether in Clojure or elsewhere, because it'll improve your programming regardless of what language you use.

Thomas Kern replied on Thu, 2012/09/06 - 10:58am

Ruby actually already has lots of support for this kind of thing. Python has most of it too, but also has a culture that discourages using it.

Passing functions in Ruby is quite easy. You'd write "&:+" rather than just "+" to pass plus as the function there (assuming you're using ActiveSupport or Ruby 1.9 for Symbol::to_proc). But an update-in function like that is easy to write in Ruby, and most of the regular function stuff is already in the standard library.


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.