Questions on quoting

Disclaimer: The following questions are not relevant for any code I'm working on, but I'm curious about any answers, since I'm still wondering about these questions despite telling me repeatedly they're not important. :smile:

  • I can use quote or ' to "nest" quoting as often as I want to, as in

    > 'a
    'a
    > ''a
    ''a
    > '''a
    '''a
    

    and so on.

    I wonder if there's also a way to "unquote" a quoted expression like this:

    > (my-unquote ''a)
    'a
    

    I haven't found anything on this. There is unquote, but it gives an error message when used outside a quasiquote form.

    The closest approach I can think of is using eval, but that seems kind of "heavy." I guess it should be possible to turn ''a into 'a at compile time. Then again, I don't see where such unquoting would be needed, so maybe there's no way to do it because nobody needed it.

  • I occasionally read the statement that quoting a literal gives the literal itself. Looking at

    > '1
    1
    > (eq? '1 1)
    #t
    

    this statement looks reasonable, but if I apply quoting more than once, I don't get the literal:

    > ''1
    ''1
    > (= 1 '1)
    #t
    > (= 1 ''1)
    ; =: contract violation
    ;   expected: number?
    ;   given: ''1
    ; [,bt for context]
    

    I guess this is something to do with the application of macros from outside in, i.e. since ''1 is equivalent to (quote (quote 1)) the outer quote "sees" (quote 1), not 1. However, I'm still somewhat confused and to me that's only a part of the explanation.

Can anyone shed some light on these two issues?

  1. I think eval is precisely the opposite of quote: quote prevents evaluation. The natural counterpart is to force evaluation using eval (though note considerations of current-namespace/the namespace argument to eval when in a module vs a REPL).

  2. (quote (quote 1)) evaluates to the list (quote 1):

> (equal? ''1 (list 'quote 1))
#t

Recall that quote is a special form, and as such has its own evaluation rules. Special forms are evaluated outside-in, so in (quote_a (quote_b 1)) (marked for clarity; assume both are actually quote) the quote_a controls evaluation of the expression (quote_b 1) and, by the rules of quote, does not evaluate it but returns it directly.

2 Likes

In fact, the latter point is what makes (eval ''a) work (assuming appropriate namespace setup): the ''a is given to eval as (quote a), and evaluating that expression is of course the result 'a.

1 Like

'<whatever> is read/parsed as (quote <whatever>). For the rest of my comment, I will use (quote <whatever>) explicitly to make it less confusing.

quote is not a function. It is a form that constructs a representation of <whatever>. It can’t be a function because if it were, <whatever> would already be evaluated by the time “quote” consumes it, so it can’t access the underlying representation. This can partially explain why (quote (quote 1)) doesn't necessarily need to be equal to (quote 1).

In Racket, some expressions have a representation that coincides with itself. Numbers, like 1, are one of these expressions. That’s why (quote 1) evaluates to 1.

On the other hand, (quote 1) is not self-quoting. (quote (quote 1)) needs to construct a representation of (quote 1), so it constructs (list (quote quote) 1).

How do you remove the outer quote when it’s nested in more than one level? (quote (quote <whatever>)) evaluates to (list (quote quote) (quote <whatever>)). As you want (quote <whatever>), you can simply access the second element.

> (quote (quote (hello world)))
''(hello world)
> (second (quote (quote (hello world))))
'(hello world)

4 Likes

Total agreement with @sorawee . To add to it: I think the printed representation

'''a

... is in many ways confusing and broken. It suggests that the symbol is somehow "triply quoted". That's basically not true; the outer quote is essentially "disabling" all of the internal quotes. I think it would be probably better to print this value as

'(quote (quote a))

... which is another way of writing the same value. More generally, I think the printer would be less confusing if it didn't "compress" quotes occurring inside of already-quoted expressions.

The counter-argument, I suppose, would be for values like

'(define (f x) (cons 'a x))

... which would then be printed as

'(define (f x) (cons (quote a) x))

Thoughts?

2 Likes

sschwarzer:

I wonder if there's also a way to "unquote" a quoted expression like this:

Since '<whatever> is read as (quote <whatever>) your "unquote" function is simply cadr.

2 Likes

Right, unless you try to use it on 'a. Then you're in syntax-land.

1 Like

You are right - I didn’t think this through. Only syntax transfers get the the reader result directly.

Here's a "dequote". @sschwarzer , is this what you're looking for?

#lang racket

(define-syntax dequote
  (syntax-rules (#%quote)
    [(_ (_ datum)) datum]))

(define a 1234)

(dequote '''a) ;; yields ''a
(dequote ''a)  ;; yields 'a
(dequote 'a)   ;; yields 1234
2 Likes

A similar problem arises when redefining quasiquote, unquote and unquote-splicing in a quoted let-form:


(define expr

(quote

(let ((quasiquote 1) (unquote 2) (unquote-splicing 3))

(+ quasiquote unquote unquote-splicing))))

(eval expr (make-base-namespace)) à 6 ok

expr à prints: (let (`1 ,2 ,@3) (+ quasiquote . ,unquote-splicing)), unreadable.

The outer regular quote does not inhibit reader abbreviation for quasiquote and acompanions.

1 Like

not unrelated: 99 ways to say '(I love you) in Racket

also - for laughs; https://twitter.com/BotRacket

source: https://cheapbotsdonequick.com/source/BotRacket

s.

1 Like

Thanks a lot for all your replies! :slight_smile: It's much clearer now.

@sorawee I knew that quote was a form, not a function, but due to the printing confusion that @jbclements mentioned, I somehow thought that quote, applied to an inner quote would handle the inner quote in some special way and I couldn't understand how exactly. Now I understand that the inner quote is just treated like any other "word" and becomes a symbol (i.e. 'quote = (quote quote)).

Having both representations, 'foo and (quote foo) can make things more difficult to follow, but I do understand that 'foo is more practical in a lot of cases. (I also use things like 'foo and '(1 2 3) all the time.)

@sorawee, along these lines, I think it was a good idea to explain everything in terms of quote instead of also using '. :+1:

Indeed, I guess that was the source of my confusion.

Yes.

In my early days with Racket, I managed to stumble into a situation like:

#lang racket/base
(define quote "To be, or not to be.")
;; many other lines of code to make this more obscure
'foo

which leads to an error message that... didn't exactly clear things up for me:

; /var/tmp/quote.rkt:7:1: foo: unbound identifier
;   in: foo

Like anything initially imported by the #lang (whether function or syntax) quote can be redefined. Add on the ' reader shorthand, and you can get enough layers to make a fine confusion.


I seem to recall an even more mysterious situation where I redefined something that caused input to the REPL to hang? Alas I can't remember now, exactly; it's probably somewhere on the old mailing list archives.

3 Likes

I think there’s a slight typo in @jbclements’s program. (dequote (1 2)) probably should not expand to 2.

Here’s a corrected version:

(define-syntax dequote
  (syntax-rules (quote)
    [(_ (quote datum)) datum]))

1 Like

Yeah, you're right, I got lazy, I couldn't be bothered to figure out what identifier would be left there. Should probably have realized that it's "quote", though :).

Ooh, but what if you stack your dequotes? Okay, actually I have work to do....

1 Like