I was trying to write a macro that compares the running times of multiple expressions, and ran into a wall. My problem can be condensed to the following code:
(defmacro test-m [& exprs]
`(map #(.toString %) ~exprs))
If I call this like (test-m 1 2 3)
, I would expect this to produce code along the lines of:
(map #(.toString %) [1 2 3])
Which is entirely valid. Unfortunately though, this actually results in an error:
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn helpers.general-helpers/eval663 (NO_SOURCE_FILE:76)
The only way I could see this possibly happening is if & exprs
results in (1 2 3)
, which would then attempt to call 1
as a function, which is obviously wrong.
How can I map over a variadic argument list in a macro?
I figured it out while writing the question, so I thought I'd post an answer in case anyone else is stuck here in the future.
The argument list needs to forced into a list representatiom first, or else it will be evaluated as a form. This can be done like:
(defmacro test-m [& exprs]
`(map #(.toString %) (list ~@exprs))
Note the exprs
are expanded via @
, and given to list
. (Thanks @amalloy for the correction).
Obvious is retrospect, but it held me up for a while. Hopefully this helps someone.