문제

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?

도움이 되었습니까?

해결책

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)))

다른 팁

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)

Matt Brown and Jens Palsberg actually provide a good definition of what unquote would be in Typed self-evaluation via intensional type functions. The eval procedure under that definition is a meta interpreter function which transforms a structured input into a different value with the same type. i.e., accept a Lisp program as an s-expression and return another s-expression as the result.

The quote form should be structure preserving such that if the program is run naturally and then the result is quoted, the resulting representation should be the same as quoting the program and then running eval.

For a more concrete example, suppose you represent JavaScript programs in JavaScript as strings (assume for simplicity that a program is a 0-argument function which returns some JavaScript object). Running this program naturally then taking the JS object output (which for example may be cyclic) and running quote on the output should return the same string as running eval on the string representation of the program.

function program() {
    ...
    return obj;
}

// If we had a true quote operation in JS, we would be
// able to run const quotedProgram = quote(program);
const quotedProgram = `
function program() {
    ...
    return obj;
}
`;

const result1 = program();
const result2 = quote(result1);
const result3 = eval(quotedProgram);
const result4 = unquote(result3);

The above example is a little weird because JS doesn't have a natural way to quote arbitrary functions as strings (toString sort of works in many cases). However, note that if quote/eval are correct, result2 and result3 should be the same; furthermore, if unquote is correct, result1 and result4 should be the same.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top