For Development HEAD DRAFTSearch (procedure/syntax/module):

4.9 Quasiquotation

Special Form: quasiquote template

[R7RS base] Quasiquotation is a convenient way to build a structure that has some fixed parts and some variable parts. See the explanation below.

Reader Syntax: `template

[R7RS] The syntax `x is read as (quasiquote x).

Special Form: unquote datum …
Special Form: unquote-splicing datum …

[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.

Reader Syntax: ,datum
Reader Syntax: ,@datum

[R7RS] The syntaxes ,x and ,@x are read as (unquote x) and (unquote-splicing x), respectively.

Quasiquote basics

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 elses 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.

Splicing

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.

Multi-argument unquotes

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.

How static are quasiquoted 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.



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT