2

In Section 13.5.4 of the Elisp reference, we are shown a problem whereby a macro definition evaluates one of its argument expressions.

(defmacro foo (a)
  (list 'setq (eval a) t))
(setq x 'b)
(foo x) → (setq b t)
     ⇒ t                  ; and b has been set.
;; but
(setq a 'c)
(foo a) → (setq a t)
     ⇒ t                  ; but this set a, not c.

We are then advised to

substitute the expression into the macro expansion, so that its value will be computed as part of executing the expansion.

I'm not sure what this means, but I interpret it as meaning that we should try to rewrite the macro as follows:

(defmacro foo (a)
  `(setq (eval ',a) t))

Which, when expanded, gives:

(macroexpand '(foo x)) → (setq (eval (quote x)) t)

But I'm getting the following error when I call (foo x):

(wrong-type-argument symbolp (eval (quote x)))

Even though when I evaluate (symbolp (eval (quote x))), the result is t.

It seems that there's something fundamental that I'm not understanding here. What's the issue?

3
  • It's a typo in the manual, it should read: "but this set c, not a." You might want to write about it to Emacs ailing list.
    – wvxvw
    Dec 15, 2015 at 6:12
  • @wvxvw: No, I don't think so. It sets a, not c. (But there is a typo: "set" should be "sets".)
    – Drew
    Dec 15, 2015 at 6:23
  • @Drew Oh, I see now, I've missed the whole point of this article :) Yeah, it's not a typo. I just wasn't careful to read it through.
    – wvxvw
    Dec 15, 2015 at 8:21

1 Answer 1

0
  1. setq does not evaluate its first argument, so your second defmacro definition cannot work. The error message tells you that setq expects a symbol as its first argument, but you passed it the list (eval (quote x)) instead.

  2. The problem with the original code is that there is variable capture: the parameter of the defmacro is a, as is the variable passed to foo.

6
  • Ah, of course, this looks like a job for set instead! And the modified macro with set does seem to work properly, including for "a" where (setq a 'c). Two questions: how are we avoiding variable capture, and don't we still have the issue of expansion during byte-compilation? How should a macro that does what we want be written? Dec 15, 2015 at 14:28
  • To avoid variable capture, the code produced by macroexpansion (and then evaluated) needs to bind new variables. See the Elisp manual, node Surprising Local Variables. (See also the other nodes about defining Lisp macros.)
    – Drew
    Dec 15, 2015 at 15:13
  • What "issue of expansion during byte-compilation"?
    – Drew
    Dec 15, 2015 at 15:13
  • From the same node: Another problem with calling ‘eval’ in a macro definition is that it probably won’t do what you intend in a compiled program. The byte compiler runs macro definitions while compiling the program, when the program’s own computations (which you might have wished to access with ‘eval’) don’t occur and its local variable bindings don’t exist. Dec 15, 2015 at 17:01
  • The way I understand it, if we have (eval a) in the macro as in the original example, the byte-compiler tries to expand the macro when the file is compiled, but a is not yet defined. I'm assuming the backquote-comma construct has the same problem, since "comma" is basically a marker shorthand for eval? Dec 15, 2015 at 17:08

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.