Clojure automatic curry

In Clojure, if you define a function like this:

(defn f
  [a b c]
  {:a a :b b :c})

And call it with

(f 1)

Clojure will tell you that you are being very naughty, and that you have not supplied the right number of parameters to the function f. Indeed, it does have every right to say so. After all, you told f to expect three parameters, and you have only supplied one.

However, in a more traditional functional programming sense, what one might mean by saying (f 1) is ‘I have only supplied f with one parameter, but I will supply it later in usage’. This is a somewhat very loose explanation of Currying.

Now Clojure doesn’t do this automatically, as we have seen above. But Clojure does have the idea of partials, which is pretty similar. Can we write a macro to generate partials automatically for a function? Yes, we can.

(defmacro defcurry
  [fname args & body]
  (let [partials (map (fn [n] `(~(subvec args 0 n) (partial ~fname ~@(take n args)) ))
                      (range 1 (count args)))]
    `(defn ~fname
       (~args ~@body)
       ~@partials)))

That’s pretty much what it takes. Now you can write an auto-currying function like this:

(defcurry f
  [a b c]
  {:a a :b b :c c})

Note how this is exactly the same syntax as using the familiar defn.

But now, with this function, you can do:

(((f 1) 2) 3)

This will return {:a 1 :b 2 :c 3} as expected. How does this work? The call to defcurry above generates the following code to be run:

(defn f
  ([a b c] {:a a :b b :c c})
  ([a] (partial f a))
  ([a b] (partial f a b)))

Simples. :)

Published: September 22 2016

blog comments powered by Disqus