[R7RS base] Quasiquotation is a convenient way to build a structure that has some fixed parts and some variable parts. See the explanation below.
`template
¶[R7RS]
The syntax `x
is read as (quasiquote x)
.
[R7RS base] These syntaxes have meaning only when they appear in the template of quasiquoted form. R5RS says nothing about these syntaxes appear outside of quasiquote. Gauche signals an error in such case, for it usually indicates you forget quasiquote somewhere.
R5RS only allows unquote
and unquote-splicing
to take
a single argument; it is undefined if you have (unquote)
or
(unquote x y)
inside quasiquoted form. R6RS allows zero
or multi-arguments, and Gauche follows that.
,datum
¶,@datum
¶[R7RS]
The syntaxes ,x
and ,@x
are read as (unquote x)
and (unquote-splicing x)
, respectively.
Suppose you want to create a list (foo bar x y)
,
where foo
and bar
are symbols, and x and y
are the value determined at runtime. (For the sake of explanation,
let’s assume we have variables x and y that provides those
values.) One way to do that is to call the function list
explicitly.
(let ((x 0) (y 1)) (list 'foo 'bar x y)) ⇒ (foo bar 0 1)
You can do the same thing with quasiquote, like this:
(let ((x 0) (y 1)) `(foo bar ,x ,y)) ⇒ (foo bar 0 1)
The difference between the two notations is that the explicit version quotes the parts that you want to insert literally into the result, while the quasiquote version unquotes the parts that you don’t want to quote.
The quasiquote version gets simpler and more readable when you have lots of static parts with scattered variable parts in your structure.
That’s why quasiquote is frequently used with
legacy macros, which are basically a procedure that
create program fragments from variable parts provided as
macro arguments. See the simple-minded my-if
macro
that expands to cond
form:
(define-macro (my-if test then else) `(cond (,test ,then) (else ,else))) (macroexpand '(my-if (< n 0) n (- n))) ⇒ (cond ((< n 0) n) (else (- n)))
Note the two else
s in the macro definition; one isn’t unquoted,
thus appears literally in the output, while another is unquoted and
the corresponding macro argument is inserted in its place.
Of course you can use quasiquotes unrelated to macros. It is a general way to construct structures. Some even prefer using quasiquote to explicit construction even most of the structure is variable, for quasiquoted form can be more concise. Gauche also tries to minimize runtime allocation for quasiquoted forms, so it may potentially be more efficient; see "How static are quasiquoted forms?" below.
When (unquote-splicing expr)
appears in a quasiquoted form,
expr must evaluate to a list, which is spliced into the
surrounding context. It’s easier to see examples:
(let ((x '(1 2 3))) `(a ,@x b)) ⇒ (a 1 2 3 b) (let ((x '(1 2 3))) `(a ,x b)) ⇒ (a (1 2 3) b) (let ((x '(1 2 3))) `#(a ,@x b)) ⇒ #(a 1 2 3 b)
Compare the unquote version and unquote-splicing version. Splicing also works within a vector.
If unquote
or unquote-splicing
takes multiple arguments,
they are interpreted as if each of its arguments are unquoted
or unquote-spliced.
;; This is the same result as `(,(+ 1 2) ,(+ 2 3) ,(+ 3 4)) `((unquote (+ 1 2) (+ 2 3) (+ 3 4))) ⇒ (3 5 7) ;; This is the same result as ;; `(,@(list 1 2) ,@(list 2 3) ,@(list 3 4)) `((unquote-splicing (list 1 2) (list 2 3) (list 3 4))) ⇒ (1 2 2 3 3 4) ;; Edge cases `((unquote)) ⇒ () `((unquote-splicing)) ⇒ ()
It is an error for zero or multiple argument
unquote
/unquote-splicing
forms appear
which you cannot splice multiple forms into.
;; Multiple arguments unquotes are error in non-splicing context `(unquote 1 2) ⇒ error `(unquote-splicing 1 2) ⇒ error
Note that the abbreviated notations ,x
and ,@x
are
only for single-argument forms. You have to write unquote
or unquote-splicing
explicitly for zero or multiple argument
forms; thus you don’t usually need to use them. These forms
are supported mainly to make the nested unquoting forms such
as ,,@
and ,@,@
—R5RS cannot handle
the case the inner unquote-splicing
form expands into zero or multiple forms.
When quasiquoted form contains variable parts, what happens at
runtime is just the same as when an explicit form is used:
`(,x ,y)
is evaluated exactly like (list x y)
.
However, Gauche tries to minimize runtime allocation when
a quasiquoted form has static parts.
First of all, if there’s no variable parts in quasiquoted
form, like `(a b c)
, the entire form is allocated statically.
If there is a static tail in the structure, it is also allocated
statically; e.g. `((,x a b) (,y c d))
works like
(list (cons x '(a b)) (cons y '(c d)))
.
Furthermore, when an unquoted expression is a
constant expression, Gauche embeds it into the static
form. If you’ve defined a constant like
(define-constant x 3)
, then the form
`(,x ,(+ x 1))
is compiled as the constant '(3 4)
.
(See Definitions, for the explanation of define-constant
form.)
In general it is hard to say which part of quasiquoted form is compiled as a static datum and which part is not, so you shouldn’t write a code that assumes some parts of the structure returned from quasiquote are freshly allocated. In other words, you better avoid mutating such structures.