quasiquote - list and vector templates

LIBRARY

(import (rnrs))                     ;R6RS
(import (rnrs base))                ;R6RS
(import (scheme r5rs))              ;R7RS
(import (scheme base))              ;R7RS

SYNOPSIS

`template
(quasiquote template)

,
unquote

,@
unquote-splicing

DESCRIPTION

Quasiquote expressions are used to create pairs, lists and vectors where some parts are static and some are evaluated at runtime. The usage is similar to string templates or string interpolation in other languages, except that quasiquote works on structures. The benefit is pretty much the same: the code closely resembles the desired result.

The backtick character is used to start a quasiquote expression. Comma is used before expressions that should be inserted in the structure. Comma at-sign is similar, but the result of the expression is a list that is spliced into the structure.

Basic examples

The list '(Hello Simon) can be created with `(Hello ,name) if the variable name has the symbol Simon. The list '(Hello Simon & Garfunkel) can be created with `(Hello ,@names) if the variable names has the value '(Simon & Garfunkel).

Nested quasiquote expressions

Quasiquote forms may be nested. Substitutions are made only for unquoted components appearing at the same nesting level as the outermost quasiquote. The nesting level increases by one inside each successive quasiquotation, and decreases by one inside each unquotation.

`(a `(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f)
          =>  (a `(b ,(+ 1 2) ,(foo 4 d) e) f)

(let ((name1 'x)
      (name2 'y))
  `(a `(b ,,name1 ,',name2 d) e))
          =>  (a `(b ,x ,'y d) e)

Abbreviations

`template is a reader abbreviation for (quasiquote template), expression is an abbreviation for (unquote expression), and ,@ is an abbreviation for (unquote-splicing expression).

The reader recognizes the abbreviations. Similarly, writer implementations often recognize opportunities to use these abbreviations, though they are not required to do so.

Here is a way to see the abbreviation: (car '`()) => quasiquote.

Multiple or zero unquoted expressions (R6RS)

R6RS extends unquote and unquote-splicing to any number of expressions. The , and ,@ abbreviations cannot express this type of unquoting.

If an (unquote expression ...) form appears inside a template, the expressions are evaluated and their results are inserted into the structure instead of the unquote form.

If an (unquote-splicing expression ...) form appears inside a template, then the expressions must evaluate to lists; the opening and closing parentheses of the lists are then stripped away and the elements of the lists are inserted in place of the unquote-splicing form.

(let ((name 'foo))
  `((unquote name name name)))
          => (foo foo foo)
(let ((name '(foo)))
  `((unquote-splicing name name name)))
          =>  (foo foo foo)

Literal parts of quasiquote expressions (R6RS & R7RS)

A quasiquote expression may return either fresh, mutable objects or literal structure for any structure that is constructed at run time during the evaluation of the expression. Portions that do not need to be rebuilt are always literal.

Thus, (let ((a 3)) `((1 2) ,a ,4 ,'five 6)) may be equivalent to either of the following expressions:

'((1 2) 3 4 five 6)

(let ((a 3))
  (cons '(1 2)
        (cons a (cons 4 (cons 'five '(6))))))

However, it is not equivalent to this expression:

(let ((a 3)) (list (list 1 2) a 4 'five 6))

If no unquote or unquote-splicing forms appear within the template, the result of evaluating (quasiquote template) is equivalent to the result of evaluating (quote template).

Grammar

The following grammar for quasiquote expressions is not context-free. It is presented as a recipe for generating an infinite number of production rules. Imagine a copy of the following rules for D = 1, 2, 3, ..., where D keeps track of the nesting depth.

\[la]template\[ra] ->
    \[la]template 1\[ra]
\[la]template 0\[ra] ->
    \[la]expression\[ra]
\[la]quasiquotation D\[ra] ->
    (quasiquote \[la]template D\[ra])
\[la]template D\[ra] ->
    \[la]lexeme datum\[ra]
  | \[la]list template D\[ra]
  | \[la]vector template D\[ra]
  | \[la]unquotation D\[ra]
\[la]list template D\[ra] ->
    (\[la]template or splice D\[ra]*)
  | (\[la]template or splice D\[ra]+ . \[la]template D\[ra])
  | \[la]quasiquotation D+1\[ra]
\[la]vector template D\[ra] ->
    #(\[la]template or splice D\[ra]*)
\[la]unquotation D\[ra] ->
    (unquote \[la]template D-1\[ra])
\[la]template or splice D\[ra] ->
    \[la]template D\[ra]
  | \[la]splicing unquotation D\[ra]
\[la]splicing unquotation D\[ra] ->
    (unquote-splicing \[la]template D-1\[ra]*)
  | (unquote \[la]template D-1\[ra]*)

In \[la]quasiquotation\[ra]s, a \[la]list template D\[ra] can sometimes be confused with either an \[la]unquotation D\[ra] or a \[la]splicing unquotation D\[ra]. The interpretation as an \[la]unquotation\[ra] or \[la]splicing unquotation D\[ra] takes precedence.

Note that R6RS extends quasiquote to support any number of expressions inside the unquoting forms. To get the R5RS/R7RS grammar: remove the * in the unquote-splicing form; remove the unquote form in the last rule; and add the rule '\[la]template D\[ra] to \[la]list template D\[ra].

IMPLEMENTATION NOTES

There are no specific notes on implementations yet. Implementations generally follow one of the RnRS specifications in how they implement quasiquote.

EVALUATION

The expression is evaluated as specified above and returned as a single value.

EXAMPLES

`(list ,(+ 1 2) 4)
             => (list 3 4)

(let ((name 'a)) `(list ,name ',name))
             => (list a (quote a))

`(a ,(+ 1 2) ,@(map abs '(4 -5 6)) b)
             => (a 3 4 5 6 b)

`((foo ,(- 10 3)) ,@(cdr '(c)) . ,(car '(cons)))
             => ((foo 7) . cons)

`#(10 5 ,(- 4) ,@(map - '(16 9)) 8)
             => #(10 5 -4 -16 -9 8)

(let ((q '((append x y) (sqrt 9))))
  ``(foo ,,@q))
             => `(foo (unquote (append x y) (sqrt 9)))

(let ((x '(2 3))
      (y '(4 5)))
  `(foo (unquote (append x y) (- 9))))
             => (foo (2 3 4 5) -9)

APPLICATION USAGE

Quasiquoting is commonly used instead of explicit list and vector calls, especially for larger structures. HTML and XML in Scheme programs is often written as quasiquote expressions. Packages like htmlprag and SXML are used to serialize the structures to text. In the nanopass compiler framework it is used to create output. String templates are not a standard part of Scheme, but can be added using macros. One such package is in the https://github.com/gwatt/dollar-sign.

COMPATIBILITY

Basic quasiquote expressions are widely portable between Scheme implementations and standards. Multiple expressions in unquote and unquote-splicing is an extension in R6RS, but is not forbidden by R7RS. The meaning of nested quasiquotation was corrected in R6RS.

ERRORS

This syntax can raise exceptions with the following condition types:
&syntax (R6RS)
It is a syntax violation if any of the identifiers quasiquote,unquote, orunquote-splicing appear in positions within a template otherwise than as described above.
&assertion (R6RS)
Raised if the unquoted expressions which must be lists do not evaluate to lists. Any unquote-splicing or multi-operand unquote form must appear only within a list or vector template.
R7RS
The assertions described above are errors. Implementations may signal an error, extend the procedure's domain of definition to include such arguments, or fail catastrophically.
R5RS
As for R7RS, except unpredictable behavior is promised.

SEE ALSO

quote(3scm)

STANDARDS

R4RS, IEEE Scheme, R5RS, R6RS, R7RS

AUTHORS

This page is part of the scheme-manpages project. It includes materials from the RnRS documents. More information can be found at https://github.com/schemedoc/manpages/.


Markup created by unroff 1.0sc,    March 04, 2023.