Combining functions with Clojure (2)

My first technical post was named Combining functions with Clojure (1) which calls for a part two :)

I actually forgot what my initial plan was, because I took so long to draft the part two :-/

In the part one I used Clojure built-in functions juxt and comp to perform several steps of transformation on a data collection.

The data collection is very simple: [:value :location :unit], and the aim is for it to undergo two steps of transformation (using the built-in functions name and clojure.string/capitalize) to get the following collection: ["Value" "Location" "Unit"].

This time I’m using Clojure built-in function partial.

Meet partial

Here’s what’s the doc says:

Takes a function f and fewer than the normal arguments to f, and returns a fn that takes a variable number of additional args. When called, the returned function calls f with args + additional args.

Afaik partial is a higher-order function as it takes in a function and returns a function. It’s quite cool and also useful!

In case you’re curious, it’s defined using nested multi-arity.

As you can see below, partial always expects a function as an argument. The multi-arity enables it to accept as many arguments as the user wants, but always with a function as its first argument. Then it returns a function, and multi-arity enables this new function to be called with the arguments given when defining the “partial function” and the arguments given when calling this new function.

(defn partial
  ([f] f)
  ([f arg1]
   (fn
     ([] (f arg1))
     ([x] (f arg1 x))
     ([x y] (f arg1 x y))
     ([x y z] (f arg1 x y z))
     ([x y z & args] (apply f arg1 x y z args))))
  ([f arg1 arg2]
   (fn
     ([] (f arg1 arg2))
     ([x] (f arg1 arg2 x))
     ([x y] (f arg1 arg2 x y))
     ([x y z] (f arg1 arg2 x y z))
     ([x y z & args] (apply f arg1 arg2 x y z args))))
  ([f arg1 arg2 arg3]
   (fn
     ([] (f arg1 arg2 arg3))
     ([x] (f arg1 arg2 arg3 x))
     ([x y] (f arg1 arg2 arg3 x y))
     ([x y z] (f arg1 arg2 arg3 x y z))
     ([x y z & args] (apply f arg1 arg2 arg3 x y z args))))
  ([f arg1 arg2 arg3 & more]
	(fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))

The way partial is used is by defining a function without arguments using def (as opposed to defn) and then call partial with a build-in Clojure function or an anonymous function:

(def ktcs-partial
  (partial #(-> %
                name
                clojure.string/capitalize)))

Here I expect this new function (ktcs-partial) to be called on each element of the collection inside a map. For this reason I want it to return a thread (Clojure function represented as a -> like here, or a ->>) of the two steps of transformation needed.

(map ktcs-partial [:value :location :unit])

("Value" "Location" "Unit")

In this case there’s no benefit over using comp for example (see blog post part 1). However when you have several steps of transformations to be executed several times, using partial in this way gets useful.

partial combined with comp

In the previous post I used the Clojure built-in function comp (for “compose”) to combine two functions (namely name and capitalize) into one.

Here I got wondering if I could define a partial function that uses comp to combine the two steps of transformation I want to perform. I’m just combining Clojure functions for the fun of it!

If I’d need this transformations performed many times and always on a collection of keywords, I’d define this function:

(def comp-partial
  (partial (fn [list-kw]
             (map #((comp clojure.string/capitalize name) %)
			 list-kw))))

And it’d be used like so:

(comp-partial [:value :location :unit])
("Value" "Location" "Unit")

So again, no revolution here. Just trying out functions so I can use them efficiently when needed!

What did I learn?

That’s it for now! Hopefully I’ll (finally) get faster at writing blog posts!


References: