Chapter 2
In order to evaluate a Scheme program, the macro uses within the
program must be expanded into core forms which the implementation can
directly further process itself. The set of core forms is
implementation-dependent: an implementation may consider any syntax form
defined by this report to be either a core form or a macro; the
difference is not observable to users. Uses of both macros and core
forms are represented as syntax objects.
Macro uses and core forms are both distinguished in forms to be
processed by syntax keywords. These keywords occupy the same namespace
as variables. That is, within the same scope, an identifier can be bound
as a variable or keyword, or neither, but not both, and local bindings
of either kind may shadow other bindings of either kind. In order to use
a macro or a core form, the corresponding keyword must be imported into
a program or library, or introduced within it by means of special macro
definition and binding forms.
Many uses of macros defined by the user have the form:
(keyword datum ...)
where keyword is an identifier which lexically refers to the
binding established for the macro or core form. Macro uses can also
take the form of improper lists, bare identifiers, or set!
forms,
where the second subform of the set!
is the keyword:
(keyword datum ... . datum)
keyword
(set! keyword datum)
In the latter case, the set!
keyword must lexically refer to the
same binding as the set!
form defined in this report, and the
binding of keyword must explicitly allow this kind of macro use
(see section 2.4).
Macros whose uses can take the form of bare identifiers are referred
to as identifier macros.
Syntax keywords are bound by user code to transformers.
Most transformers are ordinary Scheme procedures, called transformer
procedures, which receive exactly one argument, a syntax object (see
section 3) representing the
form of a macro use, and return exactly one value, a syntax object
representing the result of expanding the input macro use. The result of
expanding the input form using the transformer procedure replaces the
macro use in the place where it occurred.
Variable transformers (section 2.4) are another
kind of transformer.
It is undefined behaviour to re-enter the dynamic extent of a call to
a transformer by the expander after it has returned once.
Note: The examples in this section use the syntax-rules
system to
create transformers, which is defined in
section 5.
(define-syntax keyword transformer expression)
- syntax
Define-syntax
binds syntax keywords in a manner analogous to how
define
binds variables. Transformer expression must be an
expression that evaluates at expand time to a transformer. The
keyword is then bound as a syntax keyword to this transformer
during the process described in section 2.6. The
created binding is visible throughout the body where define-syntax
is used, unless shadowed by another binding construct within the body.
Examples:
An implication of the left-to-right processing order
(section 2.6) is that one definition can affect
whether a subsequent form is also a definition.
(splicing-let-syntax syntax bindings
definition or expression ...)
- syntax
Syntax: Syntax bindings has the form:
((keyword transformer expression) ...)
Transformer expression is as for define-syntax
. It is a syntax
violation if the same identifier (in the sense of
bound-identifier=?
) appears as the keyword of more than one of
the syntax bindings.
Semantics: The definition or expression forms are expanded in a
syntactic environment containing the bindings of the syntactic
environment of the splicing-let-syntax
form with additional bindings
created by associating each of the keywords as syntax keywords to
transformers obtained by evaluating the corresponding transformer
expressions. The evaluation of the transformer expressions takes
place within the lexical environment where the splicing-let-syntax
form appears.
The definition or expression forms are treated as if wrapped in an
implicit begin
; thus definitions created as a result of expanding
the forms have the same extent a definition which appeared in the
place of the splicing-let-syntax
would have.
Note: This form was called let-syntax
in R6RS and had the additional
restriction that the forms had to be either definitions or
expressions, but not both.
(splicing-letrec-syntax syntax bindings
definition or expression ...)
- syntax
Syntax: Same as for splicing-let-syntax
.
Semantics: The definition or expression forms are expanded in a
syntactic environment containing the bindings of the syntactic
environment of the splicing-letrec-syntax
form with additional
bindings created by associating each of the keywords as syntax
keywords to transformers obtained by evaluating the corresponding
transformer expressions. The evaluation of the transformer
expressions takes place within a lexical environment which contains
the bindings of the keywords themselves, so the transformers can
transcribe forms into uses of the macros introduced by the
splicing-letrec-syntax
form. It is undefined behaviour if the
evaluation of any of the transformer expressions requires
knowledge of the actual transformer bound to one of the keywords.
As for splicing-let-syntax
, the definition or expression forms
are treated as if wrapped in an implicit begin
and can expand into
definitions visible outside of the splicing-letrec-syntax
form
itself.
Note: This form was called letrec-syntax
in R6RS and had similar
restrictions on its contents to that report’s let-syntax
, as
described above.
(let-syntax syntax bindings body)
- syntax
Syntax: The syntax bindings are the same as for
splicing-let-syntax
and splicing-letrec-syntax
.
Semantics: The syntactic environment in the location of the
let-syntax
expression is extended by new syntax keyword bindings in
the manner of splicing-let-syntax
and the body expanded within
that environment. Let-syntax
differs from splicing-let-syntax
in
that it creates a new lexical body which is not spliced into a
surrounding body: definitions within the body are not visible
outside of the extent of the body itself.
Example: Compare this example with the example under
splicing-let-syntax
.
Implementation:
(define-syntax let-syntax
(syntax-rules ()
((_ bindings body_0 body_1 ...)
(splicing-let-syntax bindings
(let () body_0 body_1 ...)))))
Note: This form is the same as the let-syntax
in the small language
report, but not the same as let-syntax
from the R6RS (see the
remark under splicing-let-syntax
). The (scheme base)
library must
export the same binding [Editorial note: as whatever large language library this
ends up in. ]
(letrec-syntax syntax bindings body)
- syntax
Syntax: The syntax bindings are the same as for
splicing-let-syntax
and splicing-letrec-syntax
.
Semantics: The syntactic environment in the location of the
letrec-syntax
expression is extended by new syntax keyword bindings
in the manner of splicing-letrec-syntax
and the body expanded
within that environment. Letrec-syntax
differs from
splicing-letrec-syntax
in that, like let-syntax
, it creates a new
lexical body which is not spliced into a surrounding body.
Implementation:
(define-syntax letrec-syntax
(syntax-rules ()
((_ bindings body_0 body_1 ...)
(splicing-letrec-syntax bindings
(let () body_0 body_1 ...)))))
Note: This form is the same as the letrec-syntax
in the small
language report, but not the same as letrec-syntax
from the R6RS
(see the remark under splicing-let-syntax
). The (scheme base)
library must export the same binding [Editorial note: as whatever large language
library this ends up in. ]
Syntax parameters
Syntax parameters are a minor variation on ordinary syntax keyword
bindings. They provide a mechanism for rebinding a macro definition
within the dynamic extent of a macro expansion.
Among other uses, this provides a convenient solution to one of the
most common types of unhygienic macro: those that reintroduce the same
unhygienic binding each time the macro is used. With syntax parameters,
instead of introducing the binding unhygienically each time, one instead
creates a single binding for the keyword, which is adjusted when the
keyword is supposed to have a different meaning. As no new bindings are
introduced, hygiene is preserved. Using a syntax parameter also provides
the advantage that the identifier for the binding can be renamed when it
is imported, if the macro user so wishes.
(define-syntax-parameter keyword transformer expression)
- syntax
Binds keyword as a parameterizable syntax keyword, using the
transformer created by evaluating the transformer expression at
expand time as the default transformer. When keyword is used
outside the context of a syntax-parameterize
body, the result is
equivalent to if that keyword had been defined using
define-syntax
.
Define-syntax-parameter
is similar to
define-syntax
, except the created binding is marked as
parameterizable.
(syntax-parameterize ((keyword transformer expression) ...)
body)
- syntax
Adjusts the keywords to use the transformer obtained by evaluating
the corresponding transformer expressions when the keywords are
used within the expansion of the body. It is a syntax violation if
any of the keyword
s refer to bindings that are not
parameterizable syntax keyword bindings.
Syntax-parameterize
differs from let-syntax
in that the binding
is not shadowed, but adjusted, and so uses of the keywords in the
expansion of body use the new transformers.
Example: The following example defines a form lambda^
which
automatically makes an early-return procedure called return
available within its body.
(define-syntax-parameter return
(erroneous-syntax "return used outside of lambda^"))
(define-syntax lambda^
(syntax-rules ()
((lambda^ formals body_0 body_1 ...)
(lambda formals
(call-with-current-continuation
(lambda (escape)
(syntax-parameterize
((return (identifier-syntax escape)))
body_0 body_1 ...)))))))
Todo: This example will probably need changing to use delimited
control operators, once it is decided what form those will take in the
Foundations.
Variable transformers are another kind of transformer besides
transformer procedures. A variable transformer is a simple container
for a procedure, created by calling make-variable-transformer
on
that procedure. Binding a syntax keyword to a variable transformer
declares to the expander that the procedure contained within it also
expects to process macro uses of the form (set! keyword
datum)
. An attempt to expand a macro use of this form whose
transformer is not a variable transformer is a syntax violation.
(make-variable-transformer proc)
- procedure
Wraps the procedure proc
in a variable transformer and returns it.
When a syntax keyword is bound to the result of invoking
make-variable-transformer
on a transformer procedure, that
transformer procedure is invoked for all macro uses with that keyword,
including when the keyword is the left-hand side of a set!
expression, which would otherwise be a syntax violation.
Rationale: If set!
worked as described for all macro transformer
procedures, many macros would mistakenly process set!
forms as if
they were macro uses with the keyword in the operator position, and
could actually produce a result if their usual syntax happened to be
of the approximate form (keyword identifier expression)
.
The result of that expansion might then turn out to be valid Scheme
code, creating unexpected behaviour in a program whose cause might be
difficult to discover. All macros would have to check explicitly for
the comparatively rare set!
case to guard against it. By
centralizing this check within the macro expander, requiring
transformers which actually expect to process set!
forms to
explicitly declare this fact, this kind of programming error becomes
impossible.
Example:
(define-syntax used-as
(make-variable-transformer
(lambda (stx)
(cond ((identifier? stx)
(quote-syntax (quote reference)))
((free-identifier=? (car (unwrap-syntax stx)) #'set!)
`(,(quote-syntax cons)
,(quote-syntax (quote assignment))
(,(quote-syntax quote)
,(cdr (unwrap-syntax
(cdr (unwrap-syntax stx)))))))
(else
`(,(quote-syntax cons) ,(quote-syntax (quote combination))
(,(quote-syntax quote)
,(cdr (unwrap-syntax stx)))))))))
(set! used-as x)
⇒(assignment x)
(used-as y)
⇒(combination y)
Identifier properties
During expansion, a set of properties can be associated with each
identifier in a Scheme program. This allows arbitrary information to
be associated with identifiers, which can be used by macro
transformers to inform their treatment of particular identifiers. For
example, the sample implementation of the syntax-case
pattern
matcher included with this report uses identifier properties to
implement pattern variables.
Each property defined on an identifier associates a key (which must
also be an identifier) with a value (which may be any object). When an
identifier binding is created by definition or by a local binding
construct, it is associated with a new, empty set of identifier
properties. If the identifier bound shadows one from a containing
lexical context, the identifier properties on the shadowed identifier
effectively become hidden within the lexical extent of the new
binding, in the same way its binding is hidden.
When an identifier property is defined on an identifier, the property
belongs only to the lexical scope in which that property is defined.
The property itself may shadow properties created on the same
identifier and with the same key in containing lexical contexts.
When an identifier is imported from a library, it brings with it a
copy of the set of identifier properties that were defined on it in
that library. Additional identifier properties may be defined on it,
and properties from the original library may be redefined within the
context in which the identifier was imported, without those
definitions or redefinitions being visible in the original library. If
the identifier was imported into a library which subsequently
re-exports it, the re-exported version has the identifier properties
as they were (re-)defined in the library which re-exports it. If the
same binding is then imported into another context from both the
original and the re-exporting library, or from multiple re-exporting
libraries which each defined their own properties on the identifier,
the identifier in that context has a set of properties which is the
union of the properties from all the libraries it is imported from.
If two properties with the same key are imported on the same
identifier, and the values of the properties are not the same in the
sense of eqv?
, it is an import error.
Note: Though identifier properties are superficially similar to a
classical Lisp feature known as symbol property lists, the two are
quite different, even though they can sometimes be used for the same
purposes. A symbol property list is typically held globally, unlike
identifier properties, which are lexically scoped to where they were
defined.
Todo: Define the interaction of identifier properties with phasing.
(define-property identifier key expression)
- syntax
Syntax: Both identifier and key must be bound identifiers.
Semantics: The expression is evaluated at expand time to produce a
single value, and an identifier property is defined on the
identifier associating the key with this value.
Operationally, when the expander encounters a define-property
form,
it creates a new lexical address within the lexical environment for a
tuple of the identifier and the lexical address for the binding of
the key. It then stores the result of evaluating the
expression in its global binding store under the new address.
(identifier-property id key)
- procedure
(identifier-property id key default)
- procedure
Returns the identifier property associated with the identifier id
whose key has the same binding as key
. If there is no such property,
it returns default
, or #f
if no default
argument was provided.
If either id
or key
is not bound, a syntax violation is signalled.
The identifier-property
procedure can only be called within the
dynamic extent of a call by the expander to a transformer. If it is
called in other situations, it is unspecified whether the procedure
will work as intended, or act as if id
or key
or the property
requested is unbound, or will signal an error [Editorial note: an assertion
violation ].
Operationally, identifier-property
first finds the lexical addresses
and of id
and key
respectively, then finds
the lexical address in the lexical environment of id
for the tuple of id
and these lexical addresses. Finally, it looks
up the address in the global binding store and returns
the value associated with it.
Note: Two identifiers which share the same binding will not
necessarily have the same identifier properties: free-identifier=?
is used to match identifier keys but not the identifiers themselves in
the binding store when looking up identifiers. This can occur when an
identifier property’s value is shadowed, or when a binding is imported
into multiple libraries or under multiple names, as in the following
example.
Expansion process
In order to expand a body (whether library, program, or other body),
the expander processes the initial forms within from left to right. How
the expander processes each form encountered depends upon the kind of
form.
[Editorial note: The following has been formulated based on the equivalent
expansion process defined by the R6RS, but assuming that R7RS will
relax the restriction on the order of definitions and expressions in
all bodies. R7RS already relaxed the restriction in library bodies
compared to R6RS. If the restriction is not relaxed within regular
bodies, only a small adjustment to the text, reverting to R6RS
semantics for those bodies, is required. ]
[Editorial note: This process does not define semantics compatible with those
prescribed for program bodies by the small language. Those semantics
will be specified in a future fascicle. ]
- macro use
-
The expander invokes the associated transformer to transform the
macro use, then recursively performs whichever of these actions are
appropriate for the resulting form.
define-syntax
or define-syntax-parameter
form
-
The expander expands and evaluates the right-hand-side expression and
binds the keyword to the resulting transformer.
define
form
-
The expander records the fact that the defined identifier is a
variable but defers expansion of the right-hand-side expression until
after all of the forms in the body have been processed.
define-property
form
-
The expander expands and evaluates the value expression and creates
or replaces a property for the key on the given identifier, associating
it with the resulting value.
begin
form
-
The expander splices the subforms into the list of body forms it is
processing.
splicing-let-syntax
or
splicing-letrec-syntax
form
-
The expander splices the inner body forms into the list of (outer)
body forms it is processing, arranging for the keywords bound by the
splicing-let-syntax
and splicing-letrec-syntax
to be visible only in the inner body forms.
- expression, i.e., nondefinition
-
The expander defers the expansion of the expression until after all the
forms in the body have been processed.
Once the rightmost form in the body has been processed, the expander
makes a second pass over the forms deferred as the right-hand sides of
variable definitions or as nondefinitions.
Note that this algorithm does not directly reprocess any form. It
requires a single left-to-right pass over the definitions followed by a
single pass (in any order) over the body expressions and deferred
right-hand sides.
The behaviour is undefined if any definition in the sequence of forms
to define any identifier whose binding is used to determine the
meaning of the undeferred portions of the definition, or of any
definition that precedes it in the sequence of forms. Similarly, the
behaviour is undefined if the evaluation of any form in the sequence
of forms uses or assigns the value of a defined variable whose
definition is to the right of that form. For example, the behaviour of
each of the following examples is undefined:
(define define 3)
(begin (define begin list))
(display (+ x 16))
(define x 32)
[Editorial note: Last example should be within a (let () ...)
if the relaxation
is accepted for all bodies. ]
The behaviour of the following example is not undefined, because the
body of the internal increase
procedure will not be evaluated by a
call to it until after the value
variable it closes over has been
defined:
(define (make-counter)
(define (increase)
(set! value (+ value 1))
value)
(define value 0)
increase)
Phases of evaluation and macro
expansion
The algorithm for processing forms in bodies outlined above requires
the expressions creating macro transformers to be evaluated before
evaluation of the Scheme program as a whole can proceed. The
environment in which such evaluation takes place is defined by
dividing the evaluation of Scheme programs into phases. Each phase is
identified by a non-negative integer, and the number of the phase in
which evaluation is currently taking place at any time is denoted
. If a macro definition appears in phase code, then its
right-hand-side expression is evaluated in phase . The
expansion and evaluation of Scheme forms after all syntax keywords
have been defined takes place at phase ; thus, at the top level
of a body, the expansion and evaluation of the right-hand sides of all
define-syntax
forms and the transformer expressions of
splicing-let-syntax
and splicing-letrec-syntax
bindings takes
place at phase .
The environment at each phase is defined as follows. The environment
at the earliest phase of evaluation contains all bindings which the
program or library has imported from other libraries. All of these
bindings, whether they are variables or syntax keywords, are available
at all phases of evaluation. All syntactic bindings created in the
course of expansion are likewise available at all phases of evaluation
within the scopes in which they are visible. Variable bindings are
available only in the phase in which they are created. It is undefined
behaviour to either attempt to access the binding of, or to rebind an
identifier which is a variable defined in a different phase.
Note: The possibility provided by the R6RS for explicit control of the
availability of imported bindings at particular phases in import specs
has been removed, because it proved unpopular with implementers and
users.