1

Consider this property list:

(defvar *some-variable* (list :foo "fooval" :bar "barval"))

This simple call:

(getf *some-variable* :foo)

yields "fooval" as expected. I define a macro which is supposed to do the same except that I can pass the name of any property to retrieve:

(defmacro my-macro (property-name)
    `(getf *some-variable* :,property-name))

Unfortunately, calling it like this:

(my-macro 'foo)

results in FOO. Why?

matt-pielat
  • 1,659
  • 3
  • 20
  • 33
  • Use MACROEXPAND to debug this. – Rainer Joswig Dec 16 '17 at 21:49
  • @RainerJoswig: I've already tried that but I'm still clueless. – matt-pielat Dec 16 '17 at 21:54
  • And what did it return? – Rainer Joswig Dec 16 '17 at 21:54
  • @RainerJoswig: `FOO ;` `NIL` – matt-pielat Dec 16 '17 at 21:56
  • no, what did macroexpand of the form return? – Rainer Joswig Dec 16 '17 at 21:57
  • 3
    No-one yet seems to have pointed out that `:,x` is, essentially, a syntax error. You can't use `,` to splice things inside the syntax of a symbol: this is as bogus as if you said `make-,foo` to try and create some symbol. If you want to construct symbols in specific packages you have to do more work than that. –  Dec 17 '17 at 14:22
  • @tfb: CLISP sees `:,x` as `:||` plus `,x` . LispWorks OTOH reports a syntax error. – Rainer Joswig Dec 17 '17 at 16:35
  • 1
    @RainerJoswig Thanks! I should have tried before posting my comment: I think I'd assumed that the conforming interpretation was CLISP's, but I'm actually not sure, perhaps it's just not clear if it is legal or not. What it *doesn't* do is what OP wanted, obviously. This kind of error tends to come from using 'language in a string' macro systems like (shudder) Jinja2. –  Dec 17 '17 at 17:06
  • @tfb: I haven't thought about whether it is legal or not. Yes, it's clear though that it would not do the intended thing. But this type of problem (comma operator in a symbol) comes up from time to time here on Stackoverflow. There is some expectation from beginners, that this might work... – Rainer Joswig Dec 17 '17 at 17:32

1 Answers1

5

Why don't you just check it out yourself:

(macroexpand-1 '(my-macro 'foo))
; ==> (getf *some-variable* :|| 'foo) ;
T

The documentation for getf says that if you give it a 4th argument it is the value when the key is not found. Since :|| (the empty symbol in the keyword package) doesn't exist it returns the supplied default foo.

So here is a function that does what you want:

(defun get-field (name)
 (getf *some-variable* 
       (intern (symbol-name name) "KEYWORD")))

(defparameter *test* 'foo)
(get-field *test*)
; ==> "fooval"

The only reason to make it a macro is to make it syntax and the main difference between syntax and a function is that the arguments are not evaluated.

(defmacro get-mfield (name)
  `(get-field ',name))

(get-mfield foo)
; ==> "fooval"

(get-mfield *test*)
; ==> nil

You get to come with literals bare, but you loose the feature that *test* is regarded as a variable and not the key :*test*

Sylwester
  • 47,942
  • 4
  • 47
  • 79