1

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?

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

4 Comments

You probably don't want to quote it, but rather write (list ~@exprs) or ~(vec exprs).
@amalloy What's the difference in the end? Quoting it seems more straight forward. I'm not trying to be argumentative; I'm legitimately curious.
Try it with x y z variables instead of 1 2 3 constants.
@amalloy Whoops! Thanks. Good catch.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.