macroslispcommon-lispclispgnu-common-lisp

Lisp: expand property name in macro


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?


Solution

  • 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*