When I first started programming I tought the order of function parameters was essentially arbitrary; just the order the author decided to structure it. But after dabbling with a number of functional programming languages I came to the conclusion that the order is in fact important.
Let me try to convince you.
When programming Clojure, you often end up chaining function calls, like
(baz (bar (foo x))), since after all, we all work on data and that data is
processed by functions. This has been totally normal, at least until the
threading/thrush operator came along,
-> (a really long time ago). So you can
replace the code with
(-> x foo bar baz) and it looks much cleaner and
obvious. It’s fantastic!
This only works for functions with the arity of 1 (single argument functions)
and by extension, for functions which take the element to be “threaded in” as
their first argument. Therefore, Clojure also has also its sibling,
(called “thread-last”, analogous to
-> being “thread-first”), which
unsurprisingly threads the value in as last argument to the specified
Working with code, we often have a
seq that we want to operate on, so that’s
what we usually thread. Unfortunately, the standard library is not very
consistent about this, since common
seq operations take the collection as
first argument, like
conj. So we could use
->. But then when we want to use some combinators like
reduce, the collection has to be provided last, which would
The reason why e.g.
assoc has the collection first is that is a multi-arity
function and can associate multiple values at once, so the order of arguments
(assoc coll :arg1 val1 :arg2 val2 :arg-n val-n) is logical. Generally,
clojure.core functions which take collections and an unspecified amount
of arguments seem to be this way, which is understandable considering how
& arguments are handled in Clojure.
To avoid the awkward mess of mixing code that uses
->>, Clojure 1.5
as->, which allows naming the argument to be threaded (I usually
go naming the argument
<>, aka “diamond”), so it can be put in the proper
place to be resolved, but this feels very much like a clumsy (albeit effective)
compromise to get around the argument order mess.
So, which “side” of the
->> split is right? Personally, I subscribe to
the thread-last school of argument order. This means that I order the arguments
in functions according to their specificity: from the most general to the least
(map f coll), which takes the function first (since it
might work on any
coll element) and then only the specific values to be
applied on. Similarly
reduce. Working this way also has the advantage that
partial can be used to pre-populate some arguments with known values and then
just operate on a function of lesser arity.
This approach is not without precedent. For languages with implicit currying like OCaml or Haskell this order is completely normal. Currying creates out of a function like
a function like
1 2 3
So when calling
(foo bar) a function is returned which takes
returns the result. So basically it’s like using
partial for every argument.
This of course means that arguments can only be supplied left to right. The
OCaml way of threading is then
coll |> map inc so the argument is threaded in
at the end, just like our friend
->> does. The actual reason for this is of
course a bit different, since
map f returns a single-arity function so it
doesn’t really matter whether threading first or last element, since they are
identical in that case.
A more accurate translation to Clojure would be
Which is silly, since we can just use the less awkward
->> in this case:
So, I definitely recommend preferring
->> as it leads to more reasonable
argument order that can better be composed with other functions. Unfortunately,
we can’t just be all happy using
->> as we’ll have to keep using
dissoc. Maybe having them with multiple arity was not
such a great idea to start with.