Pointfree vs. Pointless

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...

Pointfree Style

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 value

Let'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...

The Cost of Pointfree Style

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.

So Pointless in the End?

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 .