
* fix cond intro 'text-expr' typo in guide * fix quote in quote title * for -> form in quasiquote unquote-splicing shorthand
166 lines
5.6 KiB
Racket
166 lines
5.6 KiB
Racket
#lang scribble/doc
|
|
@(require scribble/manual scribble/eval "guide-utils.rkt")
|
|
|
|
@(define qq (racket quasiquote))
|
|
@(define uq (racket unquote))
|
|
|
|
@title[#:tag "qq"]{Quasiquoting: @racket[quasiquote] and @racketvalfont{`}}
|
|
|
|
@refalso["quasiquote"]{@racket[quasiquote]}
|
|
|
|
The @racket[quasiquote] form is similar to @racket[quote]:
|
|
|
|
@specform[(#,qq datum)]
|
|
|
|
However, for each @racket[(#,uq _expr)]
|
|
that appears within the @racket[_datum], the @racket[_expr] is
|
|
evaluated to produce a value that takes the place of the
|
|
@racket[unquote] sub-form.
|
|
|
|
@examples[
|
|
(eval:alts (#,qq (1 2 (#,uq (+ 1 2)) (#,uq (- 5 1))))
|
|
`(1 2 ,(+ 1 2), (- 5 1)))
|
|
]
|
|
|
|
This form can be used to write functions that build lists according to
|
|
certain patterns.
|
|
|
|
@examples[
|
|
(eval:alts (define (deep n)
|
|
(cond
|
|
[(zero? n) 0]
|
|
[else
|
|
(#,qq ((#,uq n) (#,uq (deep (- n 1)))))]))
|
|
(define (deep n)
|
|
(cond
|
|
[(zero? n) 0]
|
|
[else
|
|
(quasiquote ((unquote n) (unquote (deep (- n 1)))))])))
|
|
(deep 8)
|
|
]
|
|
|
|
Or even to cheaply construct expressions programmatically. (Of course, 9 times out of 10,
|
|
you should be using a @seclink["macros"]{macro} to do this
|
|
(the 10th time being when you're working through
|
|
a textbook like @hyperlink["http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/"]{PLAI}).)
|
|
|
|
@examples[(define (build-exp n)
|
|
(add-lets n (make-sum n)))
|
|
|
|
(eval:alts
|
|
(define (add-lets n body)
|
|
(cond
|
|
[(zero? n) body]
|
|
[else
|
|
(#,qq
|
|
(let ([(#,uq (n->var n)) (#,uq n)])
|
|
(#,uq (add-lets (- n 1) body))))]))
|
|
(define (add-lets n body)
|
|
(cond
|
|
[(zero? n) body]
|
|
[else
|
|
(quasiquote
|
|
(let ([(unquote (n->var n)) (unquote n)])
|
|
(unquote (add-lets (- n 1) body))))])))
|
|
|
|
(eval:alts
|
|
(define (make-sum n)
|
|
(cond
|
|
[(= n 1) (n->var 1)]
|
|
[else
|
|
(#,qq (+ (#,uq (n->var n))
|
|
(#,uq (make-sum (- n 1)))))]))
|
|
(define (make-sum n)
|
|
(cond
|
|
[(= n 1) (n->var 1)]
|
|
[else
|
|
(quasiquote (+ (unquote (n->var n))
|
|
(unquote (make-sum (- n 1)))))])))
|
|
(define (n->var n) (string->symbol (format "x~a" n)))
|
|
(build-exp 3)]
|
|
|
|
The @racket[unquote-splicing] form is similar to @racket[unquote], but
|
|
its @racket[_expr] must produce a list, and the
|
|
@racket[unquote-splicing] form must appear in a context that produces
|
|
either a list or a vector. As the name suggests, the resulting list
|
|
is spliced into the context of its use.
|
|
|
|
@examples[
|
|
(eval:alts (#,qq (1 2 (#,(racket unquote-splicing) (list (+ 1 2) (- 5 1))) 5))
|
|
`(1 2 ,@(list (+ 1 2) (- 5 1)) 5))
|
|
]
|
|
|
|
Using splicing we can revise the construction of our example expressions above
|
|
to have just a single @racket[let] expression and a single @racket[+] expression.
|
|
|
|
@examples[(eval:alts
|
|
(define (build-exp n)
|
|
(add-lets
|
|
n
|
|
(#,qq (+ (#,(racket unquote-splicing)
|
|
(build-list
|
|
n
|
|
(λ (x) (n->var (+ x 1)))))))))
|
|
(define (build-exp n)
|
|
(add-lets
|
|
n
|
|
(quasiquote (+ (unquote-splicing
|
|
(build-list
|
|
n
|
|
(λ (x) (n->var (+ x 1))))))))))
|
|
(eval:alts
|
|
(define (add-lets n body)
|
|
(#,qq
|
|
(let (#,uq
|
|
(build-list
|
|
n
|
|
(λ (n)
|
|
(#,qq
|
|
[(#,uq (n->var (+ n 1))) (#,uq (+ n 1))]))))
|
|
(#,uq body))))
|
|
(define (add-lets n body)
|
|
(quasiquote
|
|
(let (unquote
|
|
(build-list
|
|
n
|
|
(λ (n)
|
|
(quasiquote
|
|
[(unquote (n->var (+ n 1))) (unquote (+ n 1))]))))
|
|
(unquote body)))))
|
|
(define (n->var n) (string->symbol (format "x~a" n)))
|
|
(build-exp 3)]
|
|
|
|
If a @racket[quasiquote] form appears within an enclosing
|
|
@racket[quasiquote] form, then the inner @racket[quasiquote]
|
|
effectively cancels one layer of @racket[unquote] and
|
|
@racket[unquote-splicing] forms, so that a second @racket[unquote]
|
|
or @racket[unquote-splicing] is needed.
|
|
|
|
@examples[
|
|
(eval:alts (#,qq (1 2 (#,qq (#,uq (+ 1 2)))))
|
|
`(1 2 (,(string->uninterned-symbol "quasiquote")
|
|
(,(string->uninterned-symbol "unquote") (+ 1 2)))))
|
|
(eval:alts (#,qq (1 2 (#,qq (#,uq (#,uq (+ 1 2))))))
|
|
`(1 2 (,(string->uninterned-symbol "quasiquote")
|
|
(,(string->uninterned-symbol "unquote") 3))))
|
|
(eval:alts (#,qq (1 2 (#,qq ((#,uq (+ 1 2)) (#,uq (#,uq (- 5 1)))))))
|
|
`(1 2 (,(string->uninterned-symbol "quasiquote")
|
|
((,(string->uninterned-symbol "unquote") (+ 1 2))
|
|
(,(string->uninterned-symbol "unquote") 4)))))
|
|
]
|
|
|
|
The evaluations above will not actually print as shown. Instead, the
|
|
shorthand form of @racket[quasiquote] and @racket[unquote] will be
|
|
used: @litchar{`} (i.e., a backquote) and @litchar{,} (i.e., a comma).
|
|
The same shorthands can be used in expressions:
|
|
|
|
@examples[
|
|
`(1 2 `(,(+ 1 2) ,,(- 5 1)))
|
|
]
|
|
|
|
The shorthand form of @racket[unquote-splicing] is @litchar[",@"]:
|
|
|
|
@examples[
|
|
`(1 2 ,@(list (+ 1 2) (- 5 1)))
|
|
]
|