0

This is only an example, I understand this can easily be achieved with a function.

Don't Work


This is what I've come up with based on the elisp manual and a couple of answers/articles that I have read. The suggestion is to use gensym, but I can't get it to output '("bread"), instead I get '(g719).

(defmacro shop (item)
  (let ((var (gensym)))
   `(let ((,var ,item ))
      (add-to-list 'shopping-list '(,var)))))

(let ((item "bread"))
     (macroexpand '(shop item)))

RESULTS:

(let
    ((g719 item))
  (add-to-list 'shopping-list
               '(g719)))

Works but...


This works when I rename the variable from item to my-item, but I would like to use the same name. I've read you're not suppose to use eval. Not sure about symbol-value?

(defmacro shop (item)
`(add-to-list 'shopping-list '(,(symbol-value item))))

(let ((my-item "bread"))
     (macroexpand '(shop my-item)))

RESULTS:

(add-to-list 'shopping-list
             '("bread"))
Rick
  • 25
  • 4

1 Answers1

1

Look at the macro expansion again:

(let
    ((g719 item))
  (add-to-list 'shopping-list
               '(g719)))

Notice how g719 is inside of a quoted list? It won’t be evaluated there, because it is quoted. Write your macro like this instead:

(defmacro shop (item)
  (let ((var (gensym)))
   `(let ((,var ,item ))
      (add-to-list 'shopping-list (list ,var)))))

By writing it as a call to the list function, you ensure that the arguments to that call will be evaluated first, and then the value is put into the list instead of the name.

db48x
  • 15,741
  • 1
  • 19
  • 23
  • Thanks for the answer. I copied your code and it does not work. The only difference is it expands to `(list g719)` instead of `'("bread")`. – Rick Apr 28 '23 at 17:44
  • That’s what it’s supposed to do. `macroexpand` doesn’t evaluate anything; that happens later. – db48x Apr 28 '23 at 17:50
  • Sorry about that, you're right. So I noticed that with your answer, I don't even need all the `let` in the macro or `gensym` huh? Because it works the same like this in the macro: `(backquote (add-to-list 'shopping-list (list ,item)))` – Rick Apr 28 '23 at 18:10
  • 1
    Yes. Just be aware that directly substituting an argument multiple times will cause it to be evaluated multiple times. That’s when you need to start using `let` to collect the value once, and once you do that you want to use `gensym` in order to avoid shadowing a name that might be in use inside an expression given to you by the caller. – db48x Apr 28 '23 at 18:41
  • What do you mean by `substituting an argument multiple times`? Do you mean using a comma to sub/evaluate? – Rick Apr 28 '23 at 18:58
  • Yes, `,foo` substitutes the expression into your backquoted code. Doing that multiple times is generally a bad idea. It doesn’t matter at all if the expression is just `"bread"`, but if it is more complicated than that then it’ll cause annoying problems. – db48x Apr 28 '23 at 19:30
  • Gotcha, I would have to put it to the test sometime to understand it more with a function as an argument that increments or changes variables. – Rick Apr 28 '23 at 20:14