Pointfree style is a way to program in a functional programming language, where the programmer describes the program flow via composition and combination of functions and not by shuffling data around. It is claimed, that this leads to clearer, more elegant programs. But sometimes this way of programming is also called pointless. Some examples revisited...
Let's first consider an example to understand, what pointfree really means. Let's say we are looking for a new apartment and want to compare two apartments by the cost of gas we need to get to work for a month. This could look like this.
(defn metric
"Compute the average monthly cost for commuting
between two places."
[a b]
(let [dist (distance a b)
both-ways (* 2 dist)
used-gas (* average-consumption both-ways)
cost (* average-gas-cost used-gas)]
; Assuming 20 work days per month...
(* 20 cost)))
This is usual notation. All arguments are spelled out. We track results in
locals. etc. Some statistics: One class is generated for metric
itself.
Otherwise it's simple local storage.
Now let's translate this to pointfree notation. First let us get rid of the result tracking.
(defn metric
"Compute the average monthly cost for commuting
between two places."
[a b]
(* 20
(* average-gas-cost
(* average-consumption
(* 2
(distance a b))))))
Here we clearly see how the result of one function flows into the next. But we still have to plumb the whole thing together. If we want to express this on a highler level with the interactions of the functions themselves, we need to two more constructs:
comp
aka. composition to create a new function which is equivalent to a
chain of functions applications as we have it above.partial
aka. partial application to create a new function which is a
specialised version of the original function, ie. one or more arguments are
fixed to some valueLet's see how we can define metric with our new tools. *Note: I'm well aware,
that the calls to *
could be merged. That's not the point. In general you
can't.*
(def metric
(comp (partial * 20)
(partial * average-gas-cost)
(partial * average-consumption)
(partial * 2)
distance))
Now we arrived at pointfree style. All bookkeeping is gone, we basically
declared on a very high-level, that metric
is a function consisting of the
composition of different other functions. And once more we get very shiny karma
points.... Well...
Before we jump too high in our joy, let's first have a closer look. When we compare the required resources to our initial example, we clearly loose. We now created 4 classes instead of one. Each of this classes must be instantiated and does nothing else, then just call another function immediately. So we get a cost in space and time.
But there is even a non-technical cost. Did you try to call doc
on our shiny
metric
? Bleh. But that's easy to fix.
(def #^{:doc
"Compute the average monthly cost for commuting
between two places."
:arglists '([a b])}
metric
(comp ...))
Iegh. We spoiled our shiny function with some really hard noise.
Is the pointfree style really cleaner? Maybe for some suitable definition of „clean“. For others certainly not. (At least not in Clojure.) But as always: it's a matter of correct application. Too much is almost always bad. But giving away a little karma and introducing some „impurity“ of sort, we can get the best of both worlds.
(defn metric
"Compute the average monthly cost for commuting
between two places."
[a b]
(->> (distance a b)
(* 2)
(* average-consumption)
(* average-gas-cost)
(* 20)))
This is almost pointfree. But it doesn't introduce new classes with the associated overhead. Also we get nice docstring and argument list information.
To conclude this post I'd like to point you to two examples. One (IMHO) [bad one]bo, where higher order functions don't add anything in clarity or cleanliness. I won't go into the details here. I prefer to show you some excellent application of point free style. Sean Devlin posted it on the Clojure group.
(defn map-vals [f coll]
(into (empty coll) (map (juxt key (comp f val)) coll)))
I'm remembered of the (in)famous „goto
considered harmful“, which is always
misunderstood.
„
Goto
“
The Apprentice always uses it.
The Journeyman always avoids it.
The Master uses it wisely.
Published by Meikel Brandmeyer on .
I'm a long-time Clojure user and the developer of several open source projects mostly involving Clojure. I try to actively contribute to the Clojure community.
My most active projects are at the moment VimClojure, Clojuresque and ClojureCheck.
Copyright © 2009-2014 All Right Reserved. Meikel Brandmeyer