Quoting programmatically (i.e., for macros)

188 Views Asked by At

I would like to be able to do this:

(mapcar #'quote '(1 2 3 4))

And get this

('1 '2 '3 '4)

However, since QUOTE is a special form, it can not be funcalled.

I've tried to macro it up:

(defmacro quoter (&rest args)
  `(loop for arg in ,@args collect (quote arg)))

(quoter '(1 2 3 4 ))

But that gets me, as I might expect....

(LOOP FOR ARG IN '(1 2 3 4)
      COLLECT 'ARG)

Converting the input into a string and then into a symbol from that won't work - I might have incoming forms, not just atoms.

I'm sure I'm missing something here. :-)

3

There are 3 best solutions below

1
On BEST ANSWER

This will produce an expansion you want:

(defmacro quoter (&rest args)
   (loop for arg in args collect `(quote ,arg)))

...but it's unlikely that such a macro is really what you want at a higher level (unless you want just to play with macros). The expansion is not a valid list form, so the only way to use it is to call MACROEXPAND yourself. And if you're going to call MACROEXPAND, why not make it a function and call that function?

2
On

If you want to transform an item to a form (quote item), then you have to provide the transformation.

For example (list 'quote item) or

`(quote ,item)

If you use a special form or a macro inside a function, then you can use the function and pass it to something like MAPCAR.

CL-USER > (mapcar #'(lambda (item) (list 'quote item)) '(1 2 3 4))

((QUOTE 1) (QUOTE 2) (QUOTE 3) (QUOTE 4))

This works also if the list is replaced with a variable.

If you want to write this as macro, then you get the problem that you get source code and you need to transform it. If you have a list, then you can transform it directly. If you have for example a variable, then you can't (since you don't know the value of this variable) and you would have include the transformation into the generated source.

Example:

CL-USER 119 > (defmacro quoter (list)
                (list 'quote (mapcar (lambda (item) (list 'quote item))
                                     (second list))))
QUOTER

CL-USER 120 > (macroexpand '(quoter '(1 2 3 4)))
(QUOTE ((QUOTE 1) (QUOTE 2) (QUOTE 3) (QUOTE 4)))
T

CL-USER 121 > (quoter '(1 2 3 4))
((QUOTE 1) (QUOTE 2) (QUOTE 3) (QUOTE 4))

But now it does not know what to do with (quoter some-variable). Now you can't transform a list - because it is not known. Now you would need to provide a macro expansion which would to the transformation at runtime...

0
On

The one-liner for this is:

(mapcar (lambda (x) `',x) '(1 2 3 4))

Note the usage of quotes. I find this form useful sometime.