Reputation: 3838
I was messing around with Clojure maps and I discovered this situation that I can't understand.
Let's say I got a map like this:
(def map-test {:name "head" :size 3})
I want to change the value of this map, in Clojure the usual way is to generate a new one with the modified data.
So I have this function:
(defn map-change
[part]
{:name (str (:name part) "-" 1) :size (:size part)})
As expected the invocation (map-change map-test)
returns: {:name "head-1", :size 3}
So I wrote this function using map
to clone the hash-map a given number of times, like this {:name "head-1" ...}{:name "head-2" ...}{:name "head-3" ...}
etc:
(defn repeat-test
[part times]
(map #({:name (str (:name part) "-" %) :size (:size part)}) (range 1 (inc times))))
But I got an exception that I cannot understand when I call (repeat-test map-test 5)
:
Wrong number of args (0) passed to: PersistentArrayMap
The debugger throws this exception when is assigning the value to :size
right after has evaluated (:size part)=>3
Here's the last part of the stacktrace:
Unhandled clojure.lang.ArityException
Wrong number of args (0) passed to: PersistentArrayMap
AFn.java: 429 clojure.lang.AFn/throwArity
AFn.java: 28 clojure.lang.AFn/invoke
REPL: 80 clj-lab-00.hobbits/repeat-test/fn
core.clj: 2644 clojure.core/map/fn
LazySeq.java: 40 clojure.lang.LazySeq/sval
LazySeq.java: 49 clojure.lang.LazySeq/seq
RT.java: 521 clojure.lang.RT/seq
core.clj: 137 clojure.core/seq
core_print.clj: 46 clojure.core/print-sequential
core_print.clj: 153 clojure.core/fn
core_print.clj: 153 clojure.core/fn
MultiFn.java: 233 clojure.lang.MultiFn/invoke
core.clj: 3572 clojure.core/pr-on
core.clj: 3575 clojure.core/pr
core.clj: 3575 clojure.core/pr
AFn.java: 154 clojure.lang.AFn/applyToHelper
....
But if I use a non anonymous function that does the same operation as the anonymous one:
(defn map-change
[part i]
{:name (str (:name part) "-" i) :size (:size part)})
(defn repeat-test
[part times]
(map #(map-change part %1) (range 1 (inc times))))
Now calling (repeat-test map-test 5)
works. Why ? What am I missing ?
Upvotes: 1
Views: 627
Reputation: 34800
The error is similar to this simplified example:
(map #({:a %}) [1 2 3])
clojure.lang.ArityException: Wrong number of args (0) passed to: PersistentArrayMap
You can expand #({:a %})
to see what code is actually being compiled and executed:
(macroexpand '#({:a %}))
;;=> (fn* [p1__21110#] ({:a p1__21110#}))
In other words, #({:a %})
expands to something like (fn [x] ({:a x}))
. The problem in the body of this function is that the map is called as a function, with no arguments.
Maps behave like functions: they are functions of their keys. But they expect at least one argument and at most two:
({:a 1} :a) ;;=> :a
({:a 1} :b 2) ;;=> 2
You didn't intend to call the map as a function at all. You just wanted to have the map. The anonymous function literal always expands into a function call and cannot yield a direct value. You can solve this several ways:
#(-> {:a %})
#(identity {:a %})
#(hash-map :a %)
#(do {:a %})
(fn [x] {:a x})
I prefer the latter, although I find the first one pretty funny. It's like saying: I want to return the thing I'm pointing at.
Upvotes: 6