It is desirable that program 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 and handle their correct use. It is anticipated that there will be two 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 and different kinds of parentheses.
One of the goals of this SRFI is to have feature implementations map naturally to the module systems of the respective Scheme implementations, where such a module system exists.
The scope of bindings for the provided features will vary over different implementations. Thus, to achieve portability, a program should only use a feature after an appropriate import clause, even if some Scheme implementations provide it by default.
SRFI 0, unlike other "require"-like library systems common in the Lisp world, separates between testing for the sheer existence of a feature and actually using it. This allows programs to be written which function in a wide variety of different Scheme implementations.
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).
Different Scheme systems provide language extensions by different means: Some have them built-in by default, some provide them via on-demand loading of code, some provide them via module systems. Combinations of these approaches also exist.
To write code that will run on several Scheme systems, it is useful to have common constructs to:
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.
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
(feature-implemented? '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 Common Lisp's
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.
It is desirable to separate the act of checking for the existence
of a feature from actually making it available for a Scheme program to
use. This enables program to make more involved decisions about what
features of the ones available it might use. Hence, the SRFI also
IMPORT-IMPLEMENTATION form which makes the
bindings provided by a feature available. Separating this from the
conditional construct also makes for more readable programs,
especially in cases where the program requires a fixed set of
features, and the only decision it makes are is to determine if qthe
features exist (and the program will run) or not (in which case it
Macro-expansion time may not be early enough to handle features
which are effectively changes to the reader used by the Scheme
implementation. Therefore, a third construct,
IMPORT-READER-SYNTAX, which may only occur at the
beginning of a syntactic processing unit, specifies its syntax.
One valid way to implement
COND-IMPLEMENTS is in
terms of a simpler
IF-IMPLEMENTS construct with
dependencies on just one feature at a time, and just one or two
branches. Why not specify
COND-IMPLEMENTS construct specified here gives
Scheme implementations more flexibility in implementing it. The
specification is intentionally ambiguous as to which clause will be
expanded in a
COND-IMPLEMENTS form. This is in order to
allow Scheme implementations to choose an especially convenient
(fastest/least memory-intensive/...) combination of implementations.
To demonstrate the utility of the conditional construct, consider the following example:
(cond-implements (srfi-a ... aaa ...) (srfi-b ... bbb ...))
where the programmer is implementing some abstraction that can use
aaa from SRFI a or can use function
bbb from SRFI b. However, the semantic fit with
aaa is substantially better, which the programmer
recognized by giving that implementation first. So if both SRFIs
exist, and the "quality" of implementation is the comparable for SRFI
a and SRFI b, then using the version with the better semantic fit
is probably more efficient. However if they both exist, but
SRFI a must be compiled and loaded from a slow reference
implementation, and SRFI b is implemented as a built-in
native-code library, it is preferable that the Scheme implementation
should use the second version.
<expression> --> (COND-IMPLEMENTS <cond-implements clause>+) <command or definition> --> (IMPORT-IMPLEMENTATION <feature identifier>+) <cond-implements clause> --> (<implementation requirement> <body>) --> (ELSE <body>) <implementation requirement> --> <feature identifier> --> (AND <implementation requirement>*) --> (OR <implementation requirement>*) --> (NOT <implementation requirement>) <feature identifier> --> srfi-<srfi number> <srfi number> --> <nonzero digit 10> <digit 10>* <nonzero digit 10> --> 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 <program> --> <syntax import>? <command or definition>* <syntax import> --> (IMPORT-READER-SYNTAX <feature identifier>+)
COND-IMPLEMENTS tests for the existence of feature implementations.
It either expands into the body of one of its clauses or signals an
error during syntactic processing.
COND-IMPLEMENTS expands into the
body of a clause whose implementation requirement the underlying
Scheme implementation can satisfy.
An implementation requirement has an obvious interpretation as a
logical formula, where the
variables have meaning TRUE if an implementation corresponding to the
named feature exists in the underlying Scheme implementation, and
FALSE otherwise. An implementation requirement is satisfied if its
formula is true under this interpretation.
It is unspecified into which body the
COND-IMPLEMENTS form expands if
several clauses have satisfiable requirements.
The existence of an implementation in no way implies that the bindings provided by it are actually visible. (But it is possible that bindings provided by implementations are visible by default.)
IMPORT-IMPLEMENTATION makes available the value and
syntactic bindings of implementations for the features specified by
its arguments. If a feature specified in an
IMPORT-IMPLEMENTATION clause does not exist, an error is
signalled during syntactic processing. If the bindings relating to a
feature implementation are already available,
IMPORT-IMPLEMENTATION is a no-op. It is an error for the
bindings imported by one or several
forms to conflict with each other. The existence test here is the
same as in
IMPORT-IMPLEMENTATIONto appear anywhere other than at toplevel.
In Scheme implementations which implement alternate reader syntaxes,
IMPORT-READER-SYNTAX allows stating that a syntactic
processing unit is written in the syntax specified by a SRFI. If a
feature specified in an
IMPORT-READER-SYNTAX clause does
not have an implementation, an error is signalled during syntactic
processing. The alternative syntax will be in effect immediately
following the closing parenthesis of the
IMPORT-READER-SYNTAX form. If the reader changes
relating to a feature implementation are already in effect,
IMPORT-READER-SYNTAX is a no-op. It is an error for the
syntaxes imported by one or several
forms to conflict with each other. The existence test here is the
same as in
It is an error for an
IMPORT-READER-SYNTAX to appear
anywhere but as the first form in a syntactic processing unit or
within that first form, either literally or as a result of macro
A feature implementation may be, for example:
The latter two possibilities allow loading or linking a feature implementation "on-demand" only for programs which actually use them. Some Scheme implementations might use the underlying package system to implement on-demand loading. Batch implementations might process a program using SRFI 0 to generate link commands. Other possibilities exist.
It is the intention that, if the implementation has a choice between equivalent "quality," it will choose the first matching clause in left-to-right order.
In Scheme implementations where feature implementations are loadable,
COND-IMPLEMENTS by itself should
merely test for the existence of implementations, but not actually
load any code, import any bindings, or make any reader changes. The
actual loading/importing only happens through
In Scheme implementations where feature implementations are not loadable,
COND-IMPLEMENTS is a simple feature test, and
IMPORT-READER-SYNTAX are no-ops if they do not signal an
The issue of alternative reader syntax is the most difficult. The
IMPORT-READER-SYNTAX makes the
assumption that one syntactic processing unit has a single syntax
throughout. It is convenient to have the processing unit specify
which syntax it is written in, rather than specifying the syntax
Since changing the reader syntax may have complex interactions with
macro expansion and other syntactic processing, the
IMPORT-READER-SYNTAX must appear at the beginning of a
processing unit. The intention is that a Scheme implementation can
determine the syntax of a processing unit by retrieving the first form
in a unit via
READ and macro-expanding that form. This
makes the proposal compatible with Scheme implementations which depend
on reading in a whole processing unit before macro expansion.
A consequence of the specification is that an initial form specifying
a syntax change can really only use
LETREC-SYNTAX, uses of macros defined by them, and
Scheme implementations that do not support changable readers need not
COND-IMPLEMENTS that appears as the first form specially.
IMPORT-READER-SYNTAX may be a simple macro.
The simplest implementation of SRFI 0 assumes that all existent features are built-in.
Nota bene: This implementation only demonstrates implementability under R5RS Scheme. It is by no means a reference implementation to be used in actual Scheme systems. Moreover, the behavior of this implementation is a significant restriction on the specification.
(define-syntax import-implementation (syntax-rules () ((import-implementation feature-id ...) (cond-implements (and feature-id ...) 42)))) (define-syntax import-reader-syntax (syntax-rules () ((import-reader-syntax feature-id ...) (cond-implements (and feature-id ...) 42))))
(The specification requires a macro-expansion time error to be signalled if a COND-IMPLEMENTS has no fulfilled clause. There is no standard way to so that, so this implementation uses the SYNTAX-ERROR form, with the assumption that an actual implementation would provide some way to do this.)
(define-syntax cond-implements (syntax-rules (and or not else srfi-0 srfi-5) ((cond-implements) (syntax-error "Unfulfilled COND-IMPLEMENTS")) ((cond-implements (else body ...)) (begin body ...)) ((cond-implements ((and) body ...) more-clauses ...) (begin body ...)) ((cond-implements ((and req1 req2 ...) body ...) more-clauses ...) (cond-implements (req1 (cond-implements ((and req2 ...) body ...) more-clauses ...)) more-clauses ...)) ((cond-implements ((or) body ...) more-clauses ...) (cond-implements more-clauses ...)) ((cond-implements ((or req1 req2 ...) body ...) more-clauses ...) (cond-implements (req1 (begin body ...)) (else (cond-implements ((or req2 ...) body ...) more-clauses ...)))) ((cond-implements ((not req) body ...) more-clauses ...) (cond-implements (req (cond-implements more-clauses ...)) (else body ...))) ((cond-implements (srfi-0 body ...) more-clauses ...) (begin body...)) ((cond-implements (srfi-5 body ...) more-clauses ...) (begin body...)) ((cond-implements (feature-id body ...) more-clauses ...) (cond-implements more-clauses ...))))
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.