Clojure has a lot of neat features that you don’t really find in other languages. Recently I’ve been exploring functions that create and return other functions!

I find these to be very useful in eliminating unnecessary code and keeping functions “to the point”. However, there are some things to be aware of when using these, which I will cover towards the end.

constantly

While this isn’t the most interesting composer, I thought this would be good just to get our feet wet with the idea that functions can return functions.

constantly (as you may imagine) produces a function that constantly returns the same value.

=> (def fives (constantly 5))
=> (fives)
5
=> (fives nil)
5
=> (fives :any "arity" 'or :type 1)
5

This function will take any value you give it and return that.

=> ((constantly nil))
nil
=> ((constantly true))
true
=> ((constantly :doggo))
:doggo
=> ((constantly 'sim))
'sim
=> ((constantly identity))
#object[clojure.core$identity 0x17bf2f9a "clojure.core$identity@17bf2f9a"]

partial

partial will create a function with partially populated arguments. This can be useful when you know the values you expect for a function.

For example, we know that m only has three values that can be incremented: :x, :y, and :z.

(defn inc-kw [kw m] (update m kw inc))
(def inc-x (partial inc-kw :x))
(def inc-y (partial inc-kw :y))
(def inc-z (partial inc-kw :z))

(defn update-winner [m]
  "The boring way... 😒"
  (cond
    (x-wins? m) (inc-kw :x m)
    (y-wins? m) (inc-kw :y m)
    (z-wins? m) (inc-kw :z m)))

(defn update-winner [m]
  "The fun way! 😆"
  (cond
    (x-wins? m) (inc-x m)
    (y-wins? m) (inc-y m)
    (z-wins? m) (inc-z m)))

You may decide to create a partial function that populates every argument.

(defn place-brick [wall color size x y]
  (assoc wall [x y] {:color color :size size}))

(def center-small-red-brick (partial place-brick {} :red :small 5 5))

But there are better ways of doing this.

(def center-small-red-brick #(place-brick {} :red :small 5 5))
(def center-small-red-brick (constantly {[5 5] {:color :red :size :small}}))

Be careful not to give your partial functions too many arguments… it doesn’t like that, and won’t tell you until you try to invoke the function.

=> (def fake-id (partial identity :one :two))
=> (fake-id)
Execution error (ArityException) at user/eval2052 (REPL:1).
Wrong number of args (2) passed to: clojure.core/identity

On the other hand, You may be tempted to create a partial function without any partial arguments. However, this will simply return that same function back to you.

=> (= identity (partial identity))
true

fnil

Since we’re already talking about pre-populated arguments, fnil is another function composer that replaces nil arguments with a predefined value.

This can be useful when you need to perform some operation, but expect nil values to be passed in. One way you might handle an add function (for example) may be like this.

=> (defn safe-add [x y]
      (+ (or x 0) (or y 0)))
=> (safe-add nil 1)
1
=> (safe-add 2 3)
5

However, fnil allows you to handle these scenarios in a much nicer way!

=> (def safe-add (fnil + 0 0))
=> (safe-add 2 nil)
2
=> (safe-add 3 4)
7

One way to look at fnil is a way to guarantee your function always has some value on every invocation.

Just be careful when trying to patch more than three arguments. fnil doesn’t support this arity.

=> (fnil + 1 2 3 4)
Execution error (ArityException) at user/eval2134 (REPL:1).
Wrong number of args (5) passed to: clojure.core/fnil

comp

comp is a really fun func! You can think of this one as a backwards thread operator.

These functions will do the exact same thing.

(defn next-odd? [m] (-> m :next inc odd?))
(def next-odd? (comp odd? inc :next))

Notice how the functions are reversed in the comp function. That’s because comp functions are evaluated from right to left. So when writing functions this way, be sure the order of the functions are correct, or you may run into some issues later on.

Finally, if you were curious:

  • Zero arguments produces the identity function
  • One argument returns the same function you provided
=> (= identity (comp))
true
=> (= + (comp +))
true

some-fn

This is probably my favorite composer. some-fn is basically a function version of or.

This function is useful when you want the first non-nil value of something, or if you have an or condition that operates on the same value.

Suppose you have a function like the following.

(defn rule-da-world [person]
  (when (or (leader? person) 
            (dictator? person)
            (has-red-button? person))
    ; Do da ruling
    ))

You’d probably extract that big or condition into another function.

(defn can-rule-da-world? [person]
  (or (leader? person)
      (dictator? person)
      (has-red-button? person)))

This is a good first step, but we can take this even further with some-fn!

(def can-rule-da-world? (some-fn leader? dictator? has-red-button?))

(defn rule-da-world [person]
  (when (can-rule-da-world? person)
    ; rule da world
    ))

What’s nice about this is that we’ve completely removed the concept of person, (or any argument name) from can-rule-da-world?! Now our argument need only to conform to leader?, dicator?, and has-red-button?.

Next is a function I use in finding the project id based on any entity it receives. We have a few assumptions here:

  1. No projects have a :project key
  2. Every other entity has a :project key
  3. If a thing doesn’t have a :project or an :id, we can assume it’s a project id
(def project-id (some-fn :project :id identity))

First, we check for a :project. If we get a non-nil value, then that value is returned. If we get nil, then we check :id. If we get a non-nil value, then that value is returned. If we get nil, then we just send back the original argument. This can be the id, nil, or potentially something completely different!

Lastly, keep in mind that this function will short circuit on the first truthy value.

=> (def throw-odd #(throw (Exception. (str "Odd: " %))))
=> (def throw-odds (some-fn even? throw-odd))
=> (throw-odds 2)
true
=> (throw-odds 3)
Execution error at user/throw-odd (REPL:1).
Odd: 3

every-pred

This function works almost the same as some-fn, just not as cool. Think of it as your functional and. If every function you provide it results in a truthy value for the argument, then the function will return true.

=> (def natural? (every-pred pos? integer?))
=> (natural? 3)
true
=> (natural? 3.1)
false

It’s important to know that this function will not return the value of the first falsy value or the last truthy value. It will always return a boolean.

=> (def has-xyz? (every-pred :x :y :z))
=> (has-xyz? {:x 1 :y 2})
false
=> (has-xyz? {:x 1 :y 2 :z 3})
true

Like some-fn, this function will also perform short-circuit evaluation on the first falsy value.

=> (def throw-odd #(throw (Exception. (str "Odd: " %))))
=> (def throw-odds (every-pred odd? throw-odd))
=> (throw-odds 2)
false
=> (throw-odds 3)
Execution error at user/throw-odd (REPL:1).
Odd: 3

What would be really cool is to have an “every” function that returns a value like some-fn. Maybe call it every-fn?

complement

Our logical composers just wouldn’t be complete without a logical not. complement quite literally takes a function and wraps it in a not form. Pretty straightforward.

Since our function is wrapped in a not, the result is always a boolean value.

This is useful when you need both the positive and negative form of a predicate.

Here are a few examples of some equivalent forms.

(def not-blank? (complement blank?))
(def not-blank? (comp not blank?))
(def not-blank? #(not (blank? %)))
(defn not-blank? [s] (not (blank? s)))

juxt

juxt creates a function that places the result of each function you provide it into a vector.

=> (def neighbors (juxt inc dec identity))
=> (neighbors 0)
[1 -1 0]
=> (neighbors 5)
[6 4 5]

It’s effectively just mapv, but with functions over a value rather than values over a function.

=> (defn neighbors-mapv [n] (mapv #(% n) [inc dec identity]))
=> (= (neighbors-mapv 5) (neighbors 5))
true

This is particularly useful when you want to sort by multiple values.

=> (sort-by (juxt :a :b) [{:a 4 :b 5} {:a 4 :b 2} {:a 3 :b 5}])
({:a 3, :b 5} {:a 4, :b 2} {:a 4, :b 5})

Keep in mind that order matters with juxt. The order of the functions is the order in which their results will be placed into the vector.

comparator

Last but not least, we have comparator. This function will take a 2-arity function and return a new function that conforms to the java.util.Comparator interface.

Basically, this function creates a function that returns 1, -1, or 0 based on a predicate.

Suppose I want to sort some books by how heavy they are, but I can only guess the weight by the type of book it is: :hardcover, :softcover, or :ebook.

We may already have a function that looks something like this, which returns either a tuple (truthy) or nil (falsy).

=> (defn heavier-than? [a b]
    (#{[:hardcover :softcover]
       [:hardcover :ebook]
       [:softcover :ebook]} 
     [a b]))
=> (heavier-than? :hardcover :hardcover)
nil
=> (heavier-than? :softcover :ebook)
[:softcover :ebook]

We can’t sort or sort-by this function, because it doesn’t directly conform to the compare interfaces required by those functions (i.e. we will get errors). However, comparator allows us to use to turn this function into a comparer!

=> (def compare-weight (comparator heavier-than?))
=> (sort compare-weight [:hardcover :softcover :ebook :hardcover])
(:hardcover :hardcover :softcover :ebook)

Note that this function may execute the compare function twice. So if comparing two objects requires a lot of resources, or if you’re comparing a very large collection, it may be better to just create your own compare function.

Testing Compositions

It’s important to be aware of how composed functions behave when using them in the context of with-redefs.

Within the scope of with-redefs, composed functions are redefined just as you’d expect.

=> (with-redefs [inc dec] 
     (let [plus-three (comp inc inc inc)]
       (plus-three 5)))
2

However, when defined outside the scope of with-redefs, composed functions behave the way they were originally defined–with-redefs has no effect on the behavior of the function.

=> (def plus-three (comp inc inc inc))
=> (with-redefs [inc dec] (plus-three 5))
8

Clean Composition

These composers are meant to help keep your code clean and easy to read–they should not make things more obscure. So when deciding between a composer or a normal fn or defn or whatever, always choose the one that you find easiest to understand.

(def add-ab-clean [m] (+ (:a m) (:b m)))
(def add-ab-meh (comp (partial reduce +) (juxt :a :b)))

Conclusion

That’s all, folks! At least, those are all the composers I could think of at this time. If you’re interested to see more examples, I have a cljex repository with some examples for most (not all) of these function composers.