by Marc Nieper-Wißkirchen
This SRFI is currently in final status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to srfi-206@nospamsrfi.schemers.org
. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.
This SRFI defines a mechanism for defining auxiliary syntax
keywords independently in different modules in such a way that
they still have the same binding so that they can be used
interchangeably as literal identifiers in
syntax-rules
and syntax-case
expressions
and can be both imported under the same name without conflicts.
Literal identifiers in syntax-rules
and syntax-case
expressions match another
identifier if and only if both identifiers have the same lexical
binding or if both identifiers are the same and both have no
lexical binding. This equivalence relation is the same as the
one defined by free-identifier=?
from R4RS and
R6RS.
Macro writers that need these literal identifiers as part of specific surrounding forms have therefore two choices. Either they match against a bound or against an unbound identifier.
Matching against an unbound identifier is problematic because an unbound identifier cannot be renamed.
Usually, as in the derived forms defined in R6RS and R7RS, the
literal identifiers are therefore bound. Their bindings are
called auxiliary syntax in R6RS and R7RS. They are bound to a
unique transformer for that syntax. In other words, two
identifiers naming auxiliary syntax match
(are free-identifier=?
) if and only if they are
bound to the same transformer.
This has a different kind of problem. Often, the names for
auxiliary syntax are either short common words
(like else) or simple symbolic identifiers
(like =>). Therefore, independent libraries may
each export an auxiliary keyword but all with the same name. This is
problematic when the bindings are not mutually the same (that
is, the identifiers are not free-identifier=?
).
This SRFI defines therefore a mechanism for independent libraries to independently export auxiliary syntax that has mutually the same bindings.
If syntax parameters are supported, this auxiliary syntax is a
syntax parameter so that it can be updated hygienically
through syntax-parameterize
.
This SRFI also defines a facility for defining auxiliary syntax in the above sense lexically locally. This can be used to bring auxiliary syntax back into scope and can be interesting for macro writers.
In the mailing list to SRFI 177, the idea of hygienic keywords (where keywords are understood in the sense of SRFI 177) has been discussed. Arguments in favor of hygienic keywords were the avoidance of any runtime overhead and that they bring in all the advantages of the usual Scheme macro hygiene. One argument against hygienic keywords was that independent libraries may want to independently export hygienic keywords under the same name without conflicts. That is now possible with this SRFI.
The following expression
(let* () (define-auxiliary-syntax foo foo) (define-syntax is-foo? (syntax-rules (foo) ((_ foo) #t) ((_ _) #f))) (let* () (is-foo? foo)))
evaluates to #t
as does the
expression
(let* () (define-auxiliary-syntax foo foo) (define-syntax is-foo? (syntax-rules (foo) ((_ foo) #t) ((_ _) #f))) (let* () (define-auxiliary-syntax bar foo) (is-foo? bar)))
On the other hand,
(let* () (define-auxiliary-syntax foo foo) (define-syntax is-foo? (syntax-rules (foo) ((_ foo) #t) ((_ _) #f))) (let () (define-syntax foo (syntax-rules ())) (is-foo? foo)))
evaluates to #f
.
(srfi 206 all)
The library declaration
(import (rename (only (srfi 206 all) foo bar) (bar baz)))
is (apart from the binding of
the define-auxiliary-syntax
identifier) equivalent to
the library declarations
(import (only (srfi 206) define-auxiliary-syntax)) (begin (define-auxiliary-syntax foo foo) (define-auxiliary-syntax baz bar))
When auxiliary syntax is syntax-parameterize
d, its
identity (as far as free-identifier=?
is concerned)
does not change.
For example, the following expression
(syntax-parameterize ((unquote (syntax-rules () ((_ e) (eval e (environment '(scheme base))))))) (let ((x '(+ 1 2))) (list `,x ,x)))
evaluates to
((+ 1 2) 3)
.
We can detect auxiliary syntax bindings and inspect their names in macros using an identifier property:
(define-syntax get-auxiliary-syntax-name (lambda (stx) (lambda (lookup) (syntax-case stx () ((_ x) #`'#,(datum->syntax #'* (lookup #'x #'auxiliary-syntax-name))))))
If identifier
is bound
to auxiliary syntax, the
expression (get-auxiliary-syntax-name identifier)
evaluates to the symbolic name of the auxiliary syntax.
This SRFI defines one syntax binding
form, define-auxiliary-syntax
, and another
binding, auxiliary-syntax-name
.
(define-auxiliary-syntax keyword symbol)
(define-auxiliary-syntax keyword)
This form is a definition that can be used wherever syntax definitions are allowed.
The second form is an abbreviation of the first form when
symbol
is just the
symbolic name of the keyword
keyword
.
The keyword
is
bound to (auxiliary) syntax named by symbol
.
A keyword
bound
through define-auxiliary-syntax
has the same
binding as another identifier (that is, they
are free-identifier=?
) if and only if the other
identifier is also bound
through define-auxiliary-syntax
and with the same
name.
To each such binding,
a SRFI
213 property is implicitly attached. The key of the property
is given by the binding of auxiliary-syntax-name
and
the value of the property is the symbolic name of the auxiliary
syntax.
It is an error explicitly to attach an identifier property to
any identifier using the key auxiliary-syntax-name
.
In a Scheme supporting syntax parameters (see SRFI 139), the auxiliary syntax are syntax parameters.
This SRFI defines two libraries, (srfi 206)
and (srfi 206 all)
.
The define-auxiliary-syntax
keyword and
and the auxiliary-syntax-name
binding are the two
identifiers exported by the library (srfi 206)
.
The library (srfi 206 all)
is a (magic) library that
can, in import sets, only be referenced in the form (only
(srfi 206 all) identifier …)
. This import set imports
the identifiers
, each bound at top-level as if
through define-auxiliary-syntax
, to auxiliary syntax
with the respective name.
In a Scheme implementation supporting this SRFI, all auxiliary
syntax, in particular the
identifiers else
, =>
, unquote
,
unquote-splicing
, _
, ...
defined in (scheme base)
behave as if defined
through define-auxiliary-syntax
with their
respective names.
Syntax parameters that have no sensible initial binding
(like yield
of SRFI
190) shall be bound through define-auxiliary-syntax
.
It is recommended to maintain a list of auxiliary syntax used by Scheme standards and implementations at the Scheme Registry.
This SRFI cannot be implemented portably in R6RS or R7RS alone. A complete sample implementation is provided by the Unsyntax implementation of the Scheme programming language.
A portable, but incomplete poor man's solution is provided in the repository. It will be expanded over time while a registry of auxiliary keywords is being built and while native implementations are catching up.
While this SRFI cannot be implemented in a portable way, in view
of the existence of the portable poor man's implementation of
the library (srfi 206 all)
, users that aim for
maximal portability are advised to solely import the
library (srfi 206 all)
as long as they do not need
the extra features from the library (srfi 206)
.
In
the original
syntax model of the syntax-case expander by R. Kent Dybvig et
al. two bound identifiers are free-identifier=?
if and only if their associated labels are the same. An
implementation following this model can designate special
labels, one for each symbol, naming auxiliary syntax. Whenever
auxiliary syntax with some name is defined the defined
identifier will be labelled with such a special label. When
such a a label is looked up, a suitable transformer is returned,
which can be created on the fly.
Credit goes to Adam Nelson, Jim Rees, Alex Shinn, and everyone else who initially discussed the idea for this SRFI on the SRFI 197 mailing list.
Special thanks go to John Cowan for reviewing a pre-draft of this SRFI, to Jim Rees for sharing his experience with implementing this SRFI, to Felix Thibault for testing and using this SRFI in his SRFI 206, and to Alex Shinn for providing extensive feedback on the mailing list.
© 2020 Marc Nieper-Wißkirchen.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.