quasiquote
The special form quasiquote
behaves a lot like quote
, allowing
you to write out literal expressions in your program, using the standard
textual representation of s-expressions. Scheme automatically constructs
the data structures. quasiquote
is much more powerful than
quote
, however, because you can write expressions that are
mostly literal, but leave holes to be filled in with values computed
at runtime.
For example, the value of the expression (quote (foo bar baz))
is a list (foo bar baz)
. Likewise, the value of the expression
(quasiquote (foo bar baz))
is a list (foo bar baz)
.
There's a big difference, though. quote
constructs an s-expression at
compile time, when the procedure containing the quote
expression
is compiled.(10)
quasiquote
constructs an s-expression at run time,
when the quasiquote
form is executed. This allows Scheme
to "customize" a data structure, so that you actually get a
different data structure each time you execute the same
quasiquote
form. You can use the unquote
operator to
specify which parts should be customized.
For example, suppose you want to write a procedure that creates a
three-element list whose first and last elements are the literal
symbols foo
and baz
, but whose middle element is the
value
of the variable bar
.
Try this in your scheme system:
Scheme>(define bar 2) baz Scheme>(quasiquote (foo (unquote bar) baz)) (foo 2 baz)
Without quasiquote
and unquote
, you could get the same
effect by replacing (quasiquote (foo (unquote bar) baz))
with
(list (quote foo) bar (quote baz))
, or the equivalent sugared
form (list 'quote foo 'baz)
. For this simple example,
that's probably at least as clear, because the use of (quasiquote ...)
and (unquote ...)
is rather clunky.
To make it easier to write quasiquoted expressions, Scheme provides a little
syntactic sugar. Just as you can use a single quote character and write
'(foo bar baz)
instead of (quote (foo bar baz)
, you can
use a backquote character (`
) to replace (quote ...)
and a comma character (,
) to replace (unquote ...)
.
Now we can do this:
Scheme>`(foo ,bar baz) (foo 2 baz)
This is much clearer. Intuitively, the backquote character means "construct an s-expresson of the following (literal) form, except where commas appear," and the comma character means "use the value of the following expression here, instead of using it literally."
Now you can see why it's called quasiquote
---it's
a way of writing "mostly quoted" expressions, instead of pure literals.
You can turn quoting off where you want to. This is particularly useful
in constructing s-expressions that are in fact mostly literal,
especially if they're complicated.
For a simple example, suppose you want to write a procedure that constructs a greeting to print to a user. The greeting is always mostly the same, but includes the current day of the week:
Scheme> (define day-of-week 'Sunday) day-of-week Scheme> (define (make-greeting) `(Welcome to the FooBar system! We hope you enjoy your visit on this fine ,day-of-week))) greet
Scheme>(make-greeting) (Welcome to the FooBar system! We hope you enjoy your visit on this fine Sunday) Scheme>(set! day-of-week 'Monday) day-of-week Scheme>(make-greeting) (Welcome to the FooBar system! We hope you enjoy your visit on this fine Monday)
You may have notice that this is somewhat similar to formatted output
in other languages you've used, like C. (C's printf
procedure
takes a string that is (mostly) quoted, but has special escape characters
in it to tell where to substitute the printed representation of runtime
values. For example, if day_of_week
holds a pointer to the
string "Sunday"
, printf("Welcome. It's %s.", day_of_week)
prints "Welcome. It's Sunday.
")
The nice thing about Scheme quasiquotation is that it works on normal data structures. For example, suppose you want to write a routine that creates an association list with several literal elements, and a several customized ones.
(define (create-shipping-employee-association name) `((name ,name) (employee-id-no ,(get-next-employee-id!)) (department shipping) (hire-date ,(get-day) ,(get-month) ,(get-year))))
(Notice that here that most of the unquoted expressions are calls to procedures, whose return values will be used. We can fill the holes in our templates with anything we want, not just variable values.)
Depending on the value of the variable the values returned by the
procedure calls, (new-shipping-employee-alist "Philboyd Studge")
will return something like
((name "Philboyd Studge") (employee-id-no 6357) (department shipping) (hire-date 18 August 1997))
Here it should be clear that quasiquote
has let us write out a
stereotyped data structure, and unquote
lets us fill in the varying
parts. More complicated examples would be make this benefit clearer,
but I'll leave them to your imagination.
unquote-splicing
Scheme provides a variant of unquote
for use when you want
to merge an unquoted list into a literal list, rather than nesting
it.
For example, suppose you want to embed a phrase in a sentence, where the phrase is a list of symbols, and the sentence is a list of symbols.
If you tried this with unquote, you'd get a nested list, rather than just a list of symbols:
Scheme> (define phrase-of-the-day '(the Lord helps those who take a big helping for themselves)) phrase-of-the-day Scheme> `(Remember that ,phrase-of-the-day) (Remember that (the Lord helps those who take a big helping for themselves))
Rather than using ,
expr)
, we can use use
(unquote-splicing
expr)
,
or the syntactically sugared form, ,@
expr.
Scheme> `(And remember that ,@phrase of the day) (And remember that the Lord helps those who take a big helping for themselves)