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: partial and comp

01.07.2011
| 4643 views |
  • submit to reddit

Clojure provides a few different options for creating functions inline: fn (or the #() reader macro), partial, and comp. When I first got started with Clojure I found I could do everything with fn and #(); and that's a good place to start. However, as I produced more Clojure code I found there were also opportunities to use both partial and comp to create more concise code.

The following examples are contrived. They will show how partial and comp can be used; however, they aren't great examples of when they should be used. As always, context is important, and you'll need to decide when (or if) you want to use either function.

The partial function takes a function and fewer than the normal arguments to the function, and returns a function that takes a variable number of additional args. When called, the returned function calls the function with the specified args and any additional args.

The partial function can often be used as an alternative to fn or #(). The following example shows how you can use either #() or partial to multiply a list of integers by .01 (convert pennies to dollars).

user=> (map #(* 0.01 %1) [5000 100 50])  

(50.0 1.0 0.5)

user=> (map (partial * 0.01) [5000 100 50])

(50.0 1.0 0.5)
In a straightforward example, such as the one above, it's really up to you which you'd prefer. However, if you want to specify a predicate that takes a variable number of args then the case for partial starts to become a bit more noticeable.
user=> (map #(apply str "price & tip: " %&) [5000 100 50] (repeat "+") [2000 40 10])

("price & tip: 5000+2000" "price & tip: 100+40" "price & tip: 50+10")

user=> (map (partial str "price & tip: ") [5000 100 50] (repeat "+") [2000 40 10])  

("price & tip: 5000+2000" "price & tip: 100+40" "price & tip: 50+10")
I haven't come across an example yet that made me think: The partial function is definitely the right choice here! However, I have passed around a few functions that had several arguments and found I prefer (partial f arg1) to #(f arg1 %1 %2 %3) and #(apply f arg1 %&).

The comp function takes a variable number of functions and returns a function that is the composition of those functions. The returned function takes a variable number of args, applies the rightmost of functions to the args, the next function (right-to-left) to the result, etc.

In a previous blog entry I used comp to return the values from a map given a list of keys. Below you can find the same example that shows the definition and usage.
user=> (def select-values (comp vals select-keys))

#'user/select-values

user=> (select-values {:a 1 :b 2} [:a])           

(1)
As you can see from the example, comp creates a function that takes a sequence of keys, calls select-keys with that sequence, then calls vals with the result of calling select-keys. As the documentation specifies, the functions are called from right to left.

In general I find myself executing some functions directly with some data. In that case I generally use the -> macro. For example, if I already have a map and I want a list of the keys I'm probably going to write code similar to what's found below.
user=> (-> {:a 1 :b 2} (select-keys [:a]) vals)

(1)
However, there are times when you need a function that is the composition of a few other functions. For example, taking a list of numbers, converting them to strings, and then converting them to keywords.
user=> (map (comp keyword str) [1 2])

(:1 :2)
The same thing can be done with #(), as the example below shows.
user=> (map #(keyword (str %1)) [1 2])

(:1 :2)
While the code above works perfectly well, I definitely prefer the version that uses the comp function.

Like so many other functions in Clojure, you can get by without partial and comp. However, I find my code more readable and maintainable when I use tools specifically designed to handle my current problem.

From http://blog.jayfields.com/2011/01/clojure-partial-and-comp.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.)

Comments

Sam Aaron replied on Fri, 2011/01/07 - 6:53am

However, I have passed around a few functions that had several arguments and found I prefer (partial f arg1) to #(f arg1 %1 %2 %3) and #(apply f arg1 %&)
.

In these cases I have always preferred to use either an inline fn - allowing me to specify names for the args which can help me communicate the intent of the code or if the fn starts become less than trivially complex I'd break out into a totally separate fn with its own name and docstring. For example, I might write the tips form as follows:

(let [prices [5000 100 50]
       seps (repeat "+")
       tips [2000 40 10]]
  (map (fn [p s t] (str "price & tip: " p s t)) prices seps tips))

Perhaps I'm yet to come across a usecase where %1 %2 %3 really don't need sensible names or abbreviations - in which case I do think your advice holds.

Thanks for your article - the Clojure community definitely benefits from the less frequented core fns being introduced in such a clear and concise manner - there are so many of them!

Comment viewing options

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