Why don't anonymous functions work with maps in clojure?

Viewed 243

When I do the following it works:

user> (#(+ % 8) 7)
15

But why doesn't this work?

user> (#({:a %}) 7)
Execution error (ArityException) at user/eval74597$fn (form-init937950372947324749.clj:760).
Wrong number of args (0) passed to: clojure.lang.PersistentArrayMap

Expected result: {:a 7}

3 Answers

Anon-fn with #() are a reader macro. If you look at the code generated, it becomes obvious, why it does not work (the body is called):

user=> '#({:a %})
(fn* [p1__8266#] ({:a p1__8266#}))

So the arg is passed as key for one map and it's called at once, since that is what the reader macro does. The usually safe way around this is using do, when you want to use the data literals:

user=> (#(do {:a %}) 7)
{:a 7}

Or, of course, using the various functions like hash-map to create the data structure.

This is because the way the anonymous function shorthand expands, which on a clojure.lang.PersistentArrayMap happens to be invalid.

The #(+ % 8) shorthand is a macro that expands to:

(fn* [x] (+ x 8))

When you call it with #({:a %}) it expands to:

(fn* [x] ({:a x}))

Which tries to invoke the map as a function without its required arguments. Alternatively you can use assoc to achieve the desired result:

user=> (#(assoc {} :a %) 7)
{:a 7}

The same reason this gives the same error:

user> (defn foo [x] ({:a x}))
#'user/foo

user> (foo 1)
Execution error (ArityException) at user/foo (form-init8381914060375571593.clj:54).
Wrong number of args (0) passed to: clojure.lang.PersistentArrayMap

You're not the only one to get tripped up by this. The issue is explained here: https://clojure.org/guides/learn/functions#_gotcha

You can either use the longer way of creating anonymous functions:

user> ((fn [x]{:a x}) 7)
{:a 7}

Or use a function to create the map dynamically:

user> (#(hash-map :a %) 7)
{:a 7}
Related