16

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?

Alexej Magura
  • 4,833
  • 3
  • 26
  • 40
SaltyEgg
  • 1,498
  • 1
  • 15
  • 26
  • 2
    Rereading your question, I get the impression that what you want isn't `unquote` per se (which is already provided, as I mentioned in my answer), but a kind of [`local-eval`](http://www.gnu.org/software/guile/manual/html_node/Local-Evaluation.html) that works the same way as JavaScript's `eval` (wherein lexical variables are available). – C. K. Young Aug 30 '13 at 14:34
  • 1
    @ChrisJester-Young Yes, that is what I mean. – SaltyEgg Aug 30 '13 at 15:58
  • @ChrisJester-Young And I do want to know, why this kind of `quote` is not a primitive. Cause there was a `quote`, why the author didn't include its invert function? – SaltyEgg Aug 30 '13 at 16:04
  • 1
    You see, the kind of `unquote` you wanted would effectively amount to a `local-eval`, which is much more complicated to implement than the simple `quote` or `quasiquote` system, and is thus unsuitable to be a primitive. – C. K. Young Aug 30 '13 at 16:27
  • Thanks. And one more question: Is there a `local-eval` in Scheme or CL? I have no clue till now. – SaltyEgg Aug 31 '13 at 03:59
  • @SaltyEgg try: `(defun unquote (quoted-sexp) quoted-sexp)`. It takes advantage of the fact that function arguments in CL are evaluated before being processed (handed over to the function). – Alexej Magura Nov 02 '17 at 20:34

3 Answers3

15

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)))
C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • Macros are expanded before runtime execution, and this kind of unquote does not take effect on a symbol in runtime environment. – SaltyEgg Aug 29 '13 at 15:48
  • It does, in Scheme, where macros don't just work on symbols, but on identifiers that are tagged with lexical information. (Yay hygienic macros!) Unless there's a specific example where you think my example doesn't address adequately. – C. K. Young Aug 29 '13 at 15:49
  • I'm not familiar with Scheme, I've tested a case in Common Lisp, and it's not work. – SaltyEgg Aug 29 '13 at 16:14
  • `(defmacro foo (x) \`(print ,(cadr x)))`(line-break here) `(foo '(+ 1 2))` ;;; this works (line-break here) `(foo l)` ;;; this not works, where l = '(+ 1 2) – SaltyEgg Aug 29 '13 at 16:17
  • 1
    @SaltyEgg I've added a Scheme implementation of `quasiquote`, anyway, for your reference (even though I don't know CL enough to write a CL version thereof). – C. K. Young Aug 29 '13 at 16:18
  • Have you tested this on runtime environment? For example, getting a S-expression from (read) and then try to unquote? I know few about Scheme so I need to ensure this. – SaltyEgg Aug 29 '13 at 16:27
  • @SaltyEgg The inability to use `(foo l)` in your example has to do with the way macros work, not to do with the way `unquote` works. If we leave out discussions of macros, how would `unquote` work in your desired scenario? – C. K. Young Aug 30 '13 at 14:31
3

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)
MadPhysicist
  • 5,401
  • 11
  • 42
  • 107
0

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.