A mistake that shows up at least once in almost every class I teach.
The Clojure reader macro #()
creates an anonymous function whose body is a single expression, and that expression must be a list.
For example, someone wants to wrap some values in vectors. They’ve just learned about anonymous functions, and they write:
(map #([%]) values)
This doesn’t work. It throws “clojure.lang.ArityException Wrong number of args (0) passed to: clojure.lang.PersistentVector.”
What’s going on? To a beginner, it’s not at all obvious. The key is “Wrong number of args.” That tells us something is being called, as a function, incorrectly. The thing being called is a PersistentVector. The only vector in the example is inside the anonymous function #([%])
. Why is the vector being called like a function?
To understand why, we need to understand the expansion of #([%])
. When learning about the #()
reader macro, it is usually presented as a shorthand syntax for fn
. And it is, just not in the way most people assume.
The parentheses of #()
become the parentheses of a function call inside the body of the anonymous function:
The example at the beginning of this post is wrong because:
That extra pair of parentheses makes all the difference. It’s as if we had written:
(map (fn [x] ([x])) values)
Notice the parentheses around the vector, ([x])
. That’s a function call, and it’s trying to call the vector.
Vectors are callable in Clojure (they are functions from indices to values) but they require an argument. That gives us the “ArityException: Wrong number of args (0).”
The #()
syntax presumes a function call (syntactically, a list), so it doesn’t work with any other literal data structure as the return value. We have to use either fn
or a constructor function like vector
.
In this particular example, we could just use the vector
function:
(map vector values)
For anything more complicated, I would use fn
:
(map (fn [x] [(str x) (int x)]) "abcdefg")
Please don’t do anything like this:
(map #(do [%]) values)
It’s cute, clever, and it even works. But don’t.
The #()
is syntactic sugar specifically designed for small anonymous functions consisting of a single function or method call. Adding do
is extra noise, and it’s inconsistent with how you would write the function using fn
.
#()
is great for tiny functions, like adding an argument:
(map #(/ % 2) (range 10))
Or Java method calls, which are not first-class functions:
(map #(.toInstant ^Date %) dates)
The general rule is: Use #() only for a single function or method call. For anything else, use fn
.