Function Composition with Partial
One thing I’ve found interesting about Clojure is how there are several ways of creating anonymous functions.
=> (map (fn [n] (+ 5 n)) [1 2 3])
=> (map #(+ 5 %) [1 2 3])
=> (map (partial + 5) [1 2 3])
; ...
(6 7 8)
All of these methods are great, and some are more useful than others in different scenarios.
- The
fn
form gives you close control over your parameters. #()
allows you to quickly define a function inline. This is useful when using functions likemap
orreduce
to operate on elements.partial
takes an existing function and composes a new function based on some default parameters that you give it.
Now it would seem that #()
can do everything that partial
can do, just with slightly
more control and fewer characters. So why bother using partial
at all? The #()
form
is much more readable (in my opinion).
I have a hypothesis, and I could be very wrong, but I believe this partial
function
was created specifically for function composition, much like how comp
is used.
Let’s take a look at comp
really quick.
=> (defn plus-ten [n] (+ 10 n))
=> (defn divide-by-three [n] (/ n 3))
=> (def divided-by-three-plus-ten (comp plus-ten divide-by-three))
=> (divided-by-three-plus-ten 5)
35/3
Notice how divided-by-three-plus-ten
is declared without parameters, yet it is a
function composed of two other functions. Similarly, I think the partial
function
was mainly intended for this sort of use case.
=> (defn log [level message] (println level "|" message))
=> (def info (partial log "Info"))
=> (def warn (partial log "Warn"))
=> (def error (partial log "Error"))
=> (log "Debug" "Some debugging")
"Debug | Some debugging"
=> (info "Some information...")
"Info | Some information..."
=> (warn "Hmm... this is fishy")
"Warn | Hmm... this is fishy"
=> (error "MAN OVERBOARD!!!")
"Error | MAN OVERBOARD!!!"
And there you have it! Function composition using partial
. While we can just as easily
do the same thing with any of the other forms…
(defn info [message] (log "Info" message))
(def warn #(log "Warn" %))
(def error (fn [message] (log "Error" message)))
…I think partial
does a good job of getting this idea of function composition across;
these are all functions composed of other functions. But whatever the original intent
of the partial
, #()
, or fn
forms were, always choose the one you think looks
best for the code your writing!