diff --git a/collects/scribblings/guide/match.scrbl b/collects/scribblings/guide/match.scrbl index 2918045db4..7025ba9f1a 100644 --- a/collects/scribblings/guide/match.scrbl +++ b/collects/scribblings/guide/match.scrbl @@ -119,6 +119,22 @@ pattern variables can be bound to lists of lists of matches: [(list (list '! x ...) ...) x]) ] + +The @racket[quasiquote] form (see @secref["qq"] for more about it) can also be used to build patterns. +While unquoted portions of a normal quasiquoted form mean regular racket evaluation, here unquoted +portions mean go back to regular pattern matching. + +So, in the example below, the with expression is the pattern and it gets rewritten into the +application expression, using quasiquote as a pattern in the first instance and quasiquote +to build an expression in the second. + +@interaction[ +#:eval match-eval +(match `{with {x 1} {+ x 1}} + [`{with {,id ,rhs} ,body} + `{{lambda {,id} ,body} ,rhs}]) +] + For information on many more pattern forms, see @racketmodname[racket/match]. Forms like @racket[match-let] and @racket[match-lambda] support diff --git a/collects/scribblings/guide/qq.scrbl b/collects/scribblings/guide/qq.scrbl index f9fa41dc49..30b674f2ff 100644 --- a/collects/scribblings/guide/qq.scrbl +++ b/collects/scribblings/guide/qq.scrbl @@ -24,6 +24,63 @@ evaluated to produce a value that takes the place of the `(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 @@ -35,6 +92,46 @@ is spliced into the context of its use. `(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