Why isn't there an `unquote` Lisp primitive?

2019-02-21 21:53发布

问题:

Lately, I've been thinking a lot about the basis of Lisp; I've read several manuals and/or other materials on the Internet, including The Roots of Lisp by P. ‎Graham:

In The Roots of Lisp, quote is described as a primitive that changes code into data, thereby quoting it, but there doesn't seem to be an equivalent inverse primitive, that is an unquote primitive. I thought it might have been eval's business, but eval often runs the data in a null lexical environment, which is not equivalent to changing data back into code.

Ergo, why isn't there an unquote Lisp primitive?

回答1:

unquote is only useful in the context of quasiquote, and quasiquote can be implemented as a macro (that uses quote behind the scenes). So there's no need to have an unquote primitive; the quasiquote macro simply deals with unquote symbols as they are found.

(quasiquote is the Scheme name for the backtick quote. Thus:

`(foo bar ,baz)

is read in as

(quasiquote (foo bar (unquote baz)))

in Scheme.)


Here's a very simple Scheme quasiquote macro (it only handles lists, unlike standard quasiquote which also handles vectors and other data types):

(define-syntax quasiquote
  (syntax-rules (unquote unquote-splicing)
    ((quasiquote (unquote datum))
     datum)
    ((quasiquote ((unquote-splicing datum) . next))
     (append datum (quasiquote next)))
    ((quasiquote (datum . next))
     (cons (quasiquote datum) (quasiquote next)))
    ((quasiquote datum)
     (quote datum))))

Equivalent version using all the standard reader abbreviations:

(define-syntax quasiquote
  (syntax-rules (unquote unquote-splicing)
    (`,datum
     datum)
    (`(,@datum . next)
     (append datum `next))
    (`(datum . next)
     (cons `datum `next))
    (`datum
     'datum)))


回答2:

I am also relatively new to Lisp, but I think that what you were thinking about is eval. evalis the way to change data back to code.

Namely, consider a simple function.

(defun foo (a b c) (list a b c))

Then, if you do something like this, you get a list of symbols:

CL-USER> (foo 'a 'b 'c)
(A B C)

If you add a quote in the front, the function call itself is treated as a piece of data (list):

CL-USER> '(foo 'a 'b 'c)
(FOO 'A 'B 'C)

Adding one more quote has an expected effect:

CL-USER> ''(foo 'a 'b 'c)
'(FOO 'A 'B 'C)

Let us now unwind it with eval, which in essence may be thought of as the inverse operation for the quote. It is the inverse. The x-axis is the data form. The y-axis is the code form. Hopefully this (somewhat stretched) analogy makes sense.

CL-USER> (eval ''(foo 'a 'b 'c))
(FOO 'A 'B 'C)

Can you guess what will happen if I chain two evals in a row? Here it is:

CL-USER> (eval (eval ''(foo 'a 'b 'c)))
(A B C)


标签: lisp eval quote