It is desirable that programs which depend on additions to standard
Scheme name those additions. SRFIs provide the specifications of
these additions ("features"), and SRFI 0 provides the means to
actually check that these features are present in the Scheme system by
means of the
cond-expand construct. It is anticipated
that there will be two main classes of features:
("Reader syntax" refers to aspects of the syntax described by the grammars in the Scheme reports.)
The former class of features will probably include most SRFIs, exemplified by the list library specified in SRFI 1. The latter class includes Unicode source code support and different kinds of parentheses.
Control over the presence of individual features will vary over different Scheme systems. A given feature may be absent or provided by default in some Scheme systems and in others some mechanism (such as an "import" clause in the code or a program configuration file, a command line option, a dependency declaration in a module definition, etc.) will be required for the feature to be present in the system.
Moreover, in some systems a given feature may be in effect throughout the entire program if it is in effect anywhere at all. Other systems may have more precise mechanisms to control the scope of a feature (this might be the case for example when a module system is supported). In general it is thus possible that a feature is in effect in some parts of the program and not in others. This allows conflicting SRFIs to be present in a given program as long as their scope do not intersect.
SRFI 0 does not prescribe a particular mechanism for controlling the presence of a feature as it is our opinion that this should be the role of a module system. We expect that future module system SRFIs will need to extend the semantics of SRFI 0 for their purposes, for example by defining feature scoping rules or by generalizing the feature testing construct.
Most Scheme systems extend the language with some additional features (such as the ability to manipulate Unicode characters and strings, to do binary I/O, or to handle asynchronous interrupts). Such features may be provided in a variety of ways including new procedures, new program syntax, and extended behavior of standard procedures and special-forms. A particular functionality may exist in several or even most Scheme systems but its API may be different (use of a procedure or special-form, name, number of parameters, etc). To write code that will run on several Scheme systems, it is useful to have a common construct to enable or disable sections of code based on the existence or absence of a feature in the Scheme system being used. For example, the construct could be used to check if a particular binary I/O procedure is present, and if not, load a portable library which implements that procedure.
Features are identified by feature identifiers. In order for the semantics of this construct to be well-defined, the feature identifier must of course refer to a feature which has a well-defined meaning. There is thus a need for a registry, independent of this SRFI, to keep track of the formal specification associated with each valid feature-identifier. The SRFI registry is used for this purpose. It is expected that features will eventually be assigned meaningful names (aliases) by the SRFI editors to make reading and writing code less tedious than when using "srfi-N" feature identifiers.
Another issue is the binding time of this construct (i.e. the moment
when it operates). It is important that the binding time be early so
that a compiler can discard the sections of code that are not needed,
and perform better static analyses. Expressing this construct through
a procedure returning a boolean, such as
'srfi-5), would not achieve this goal, as its binding time is
too late (i.e. program run-time). A read-time construct, such as
#+ read-macro, is very early but would
require non-trivial changes to the reader of existing Scheme systems
and the syntax is not particularly human friendly. Instead, a
macro-expansion-time construct is used.
The construct is restricted to the top level of a program in order to simplify its implementation and to force a more disciplined use of the construct (to facilitate reading and understanding programs) and to avoid (some) misunderstandings related to the scope of features. These restrictions can of course be lifted by some Scheme systems or by other SRFIs (in particular module system SRFIs).
<command or definition> --> <command> | <definition> | <syntax definition> | (begin <command or definition>+) | <conditional expansion form> <conditional expansion form> --> (cond-expand <cond-expand clause>+) | (cond-expand <cond-expand clause>* (else <command or definition>*)) <cond-expand clause> --> (<feature requirement> <command or definition>*) <feature requirement> --> <feature identifier> | (and <feature requirement>*) | (or <feature requirement>*) | (not <feature requirement>) <feature identifier> --> a symbol which is the name or alias of a SRFI
cond-expand form tests for the existence of
features at macro-expansion time. It either expands into the body of
one of its clauses or signals an error during syntactic processing.
cond-expand expands into the body of the first clause whose
feature requirement is currently satisfied (the
if present, is selected if none of the previous clauses is selected).
A feature requirement has an obvious interpretation as a logical
formula, where the
<feature identifier> variables
have meaning TRUE if the feature corresponding to the feature
identifier, as specified in the SRFI registry, is in effect at the
location of the
cond-expand form, and FALSE otherwise. A
feature requirement is satisfied if its formula is true under this
(cond-expand ((and srfi-1 srfi-10) (write 1)) ((or srfi-1 srfi-10) (write 2)) (else)) (cond-expand (command-line (define (program-name) (car (argv)))))
The second example assumes that
command-line is an alias
for some feature which gives access to command line arguments. Note that
an error will be signaled at macro-expansion time if this feature
is not present.
The simplest implementation of SRFI 0 assumes that all existent
features are built-in (here we assume
srfi-5 are present):
(define-syntax cond-expand (syntax-rules (and or not else srfi-0 srfi-5) ((cond-expand) (syntax-error "Unfulfilled cond-expand")) ((cond-expand (else body ...)) (begin body ...)) ((cond-expand ((and) body ...) more-clauses ...) (begin body ...)) ((cond-expand ((and req1 req2 ...) body ...) more-clauses ...) (cond-expand (req1 (cond-expand ((and req2 ...) body ...) more-clauses ...)) more-clauses ...)) ((cond-expand ((or) body ...) more-clauses ...) (cond-expand more-clauses ...)) ((cond-expand ((or req1 req2 ...) body ...) more-clauses ...) (cond-expand (req1 (begin body ...)) (else (cond-expand ((or req2 ...) body ...) more-clauses ...)))) ((cond-expand ((not req) body ...) more-clauses ...) (cond-expand (req (cond-expand more-clauses ...)) (else body ...))) ((cond-expand (srfi-0 body ...) more-clauses ...) (begin body ...)) ((cond-expand (srfi-5 body ...) more-clauses ...) (begin body ...)) ((cond-expand (feature-id body ...) more-clauses ...) (cond-expand more-clauses ...))))
Note that the specification requires a macro-expansion time error to
be signalled if a
cond-expand form has no fulfilled
clause. There is no standard way to do that, so this implementation
syntax-error form, with the assumption that an
actual implementation would provide some way to do this.
This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Scheme Request For Implementation process or editors, except as needed for the purpose of developing SRFIs in which case the procedures for copyrights defined in the SRFI process must be followed, or as required to translate it into languages other than English.
The limited permissions granted above are perpetual and will not be revoked by the authors or their successors or assigns.
This document and the information contained herein is provided on an "AS IS" basis and THE AUTHOR AND THE SRFI EDITORS DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.