4
votes

Here is simplified example from book On Lisp by Paul Graham (scheme like syntax).

(define-macro (bar)
  (let ((x 10) (y '(1 2 3)) (z 'foo))
    `(list ,x `(,',z ,,@y))))

I know how ,,@y should work but not sure exactly how ,',z should work what should be evaluated first and in what order. (I know it should evaluate to symbol foo because it return (10 (foo 1 2 3)) in guile, but I'm not sure what are exact steps).

I need this for my lisp in JavaScript where I have result:

(10 ((unquote z) 1 2 3))

because it just evaluate it form left to right (I'm only handling specially ,, and more commas). How should you evaluate this expression.

There is also this example in the book:

(defmacro propmacro (propname)
   `(defmacro ,propname (obj)
       `(get ,obj ',',propname)))

how ',', should be evaluated? What are the steps in this case?

Are there any other weird edge cases with backquote/quasiquote? Can you show examples of those and how they should be evaluated and in what order?

1
Google "lisp nested backquote" and you'll find many explanations.Barmar
In particular, this one is quite complete: 3e8.org/pub/scheme/doc/…coredump

1 Answers

8
votes

How ,',z works is that:

`(list ,x `(,',z ,,@y))))
          ^ ^
          |  `- this comma
           `- belongs to this backquote

The above comma interpolates, into the inner backquote, the expression ',z or (quote ,z). And that ,z, in turn, belongs to the outer backquote.

Thus the value of z is inserted into (quote ,z) to make (quote <value-of-z>).

Then, effectively, the inner backquote then behaves like `(,'<value-of-z>).

Concretely, suppose z contains the list (+ 2 2). Then we can understand it in terms of the outer backquote inserting (+ 2 2) into the inner one to produce `(,'(+ 2 2) ...). This is now straightforward to understand: when the inner backquote is evaluated, the (+ 2 2) is protected from evaluation, resulting in the object ((+ 2 2) ...).

The pattern ,',',', ... ,',expr is used to obtain a single evaluation of expr during the evaluation of the outermost backquote, such that this value is then propagated through any number of evaluation rounds of the remaining backquote nestings without undergoing further evaluation. There is a kind of "backquote algebra" at play here in which the "commas and quotes cancel out".

You can also visualize the ,',','... as a kind of drill bit that digs through the layers of nesting to allow you plant a literal value anywhere in the structure. E.g.

(defmacro super-nested-macro (arg)
  `(... `(.... `(.....`(we simply want arg down here ,',',',arg)))))

The author of super-nested-macro just wants to stick the value of arg into the template, in a position that is buried in three other backquotes. Thus the usual ,arg cannot be used: that comma would be misinterpreted as belonging to the inner-most backquote.

Are there any other weird edge cases with backquote/quasiquote?

One weird edge case in backquote is trying to splice into a dot position:

`(a b c . ,@foo)  ;; not allowed

`(a b c . ,foo)   ;; OK: equivalent to `(a b c ,@foo)

Not sure how various implementations deal with a backquote in a dot position:

`(a b c . `(d e f))

It doesn't really make sense, and I suspect that the actual result obtained will depend on the backquote implementation internals.

Not all objects are traversed for unquoting:

 `#c(,(sin theta) ,(cos theta)) ;; Not required by ANSI CL, oops!

This could work via an implementation's extension.