4

This is not my 'production code' but a simplication of the problem for illustration purposes. Also, the title of this question is misleading because it brings to mind the ~@ expansion, which I understand, and which may not necessarily be the problem. Please suggest a better question title if you can.

Given a macro with the following form:

(defmacro my-add [x & ys] `(+ ~x ~@ys))

Now let's say we have a list:

(def my-lst '(2 3))

Now I want a function that uses my-add that I can pass my-lst to as an arg, i.e.

(call-my-add 1 my-lst)

I define the function in what would seem to be the obvious way:

(defn call-my-add [x ys]
    (apply my-add (cons x ys)))

But:

java.lang.Exception: Can't take value of a macro: #'user/call-my-add (repl-1:60)

I've tried all sorts of wild tricks to get the call-my-add function to work using evals, applies, and even defining call-my-add as a macro, but they all give similar ClassCastExceptions.

Is there any way out of this?

2 Answers 2

7

No. Macros do not, cannot, will never, have access to the actual run-time values contained in their arguments, so cannot splice them into the expansion. All they get is the symbol(s) you pass to them, in this case my-list. The "way around this" is to define my-add as a function, and then (optionally) have a macro that calls that function in order to generate its code.

I wrote a blog post about this semi-recently that you might find enlightening.

You could do it with evals if you wanted to, but that is a terrible idea in almost every case:

(let [my-list '(1 2)]
  (eval `(my-add 5 ~@my-list)))
Sign up to request clarification or add additional context in comments.

3 Comments

psssst... macroes can read the arguments passed to them at macro expansion time just fine, to clarify what they cant read if the values passed to any code produced by the macro (because the macro has already finished)
Thanks amalloy. But I can make a macro as follows: (defmacro blah [x] (let [y (eval x)] `(println ~y))) which can evaluate the argument before templating it. So the macro appears to have access to the values, right?
That's...a little bit true, but I'd categorize it mostly as misleading. If x happens to be a top-level def'd var or an expression composed only of such vars, then yes, it will work. But using eval in the macro scope won't work in most cases because it doesn't have access to the local bindings from the calling scope. Specifically, (let [x 10] (blah x)) won't work with your blah macro, because x isn't in scope when (eval 'x) is called.
2

What a great example showing that macros are not first class citizens in Clojure (or any Lisp that I know of). They cannot be applied to functions, stored in containers, or passed to functions etc. In exchange for this they get to control when and if their arguments are evaluated.

What happens at macro expansion time must stay in macro expansion time. So if my-add is being evaluated at macro expansion time and you want to use apply then you need... another macro; to do the applying.

(defmacro call-my-add [x ys]
   `(my-add ~@(cons x ys))) 

Macros are somewhat contagious in this way.

PS: I'm not at my repl so please edit if you see a bug in this example (or I'll fix it when I get back)

1 Comment

I'm not sure what you're getting at here. This doesn't solve the OP's problem. (call-my-add 1 (3 5)) will work, but only because (3 5) was a literal. If you (def my-list '(3 5)), (call-my-add 1 my-list) will fail because my-list, the symbol, is not a sequence.

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.