This SRFI is currently in ``withdrawn'' status. To see an explanation of each status that a SRFI can hold, see here. It will remain in draft status until 2006/04/02, or as amended. To export input on this SRFI, please
This SRFI is being submitted by members of the Scheme Language Editor's Committee as part of the R6RS Scheme standardization process. The purpose of such ``R6RS SRFIs'' is to inform the Scheme community of features and design ideas under consideration by the editors and to allow the community to give the editors some direct feedback that will be considered during the design process.
At the end of the discussion period, this SRFI will be withdrawn. When the R6RS specification is finalized, the SRFI may be revised to conform to the R6RS specification and then resubmitted with the intent to finalize it. This procedure aims to avoid the situation where this SRFI is inconsistent with R6RS. An inconsistency between R6RS and this SRFI could confuse some users. Moreover it could pose implementation problems for R6RS compliant Scheme systems that aim to support this SRFI. Note that departures from the SRFI specification by the Scheme Language Editor's Committee may occur due to other design constraints, such as design consistency with other features that are not under discussion as SRFIs.
mailto:srfi minus 83 at srfi dot schemers dot org. See instructions here to subscribe to the list. You can access previous messages via the archive of the mailing list.
The module system presented here is designed to let programmers share libraries, i.e., code that is intended to be incorporated into larger programs, and especially into programs that use library code from multiple sources. The module system supports macro definitions within modules, allows macro exports, and distinguishes the phases in which definitions and imports are needed. This SRFI defines a standard notation for libraries, a semantics for library expansion and execution, and a simple format for sharing libraries.
export forms could
be allowed in macro expansions. In that case, the exports and imports
of a module can be determined only by macro-expanding the
Better support for records may be useful, possibly through a
(co-export <co-spec>*), where
<co-spec> contains an identifier defined or
imported into a library followed by a list of other identifiers
defined or imported into the library:
<co-spec> = (<identifier> <identifier>*)
The meaning of a
<co-spec> is that if the
first identifier becomes exported, either through
co-export, then the
remaining identifiers are also exported. Similarly, if the
first identifier is indirectly exported through
then remaining identifiers are indirectly exported.
To be useful for managing records, probably
co-export must be allowed in macro expansions. In
that case, the full exports of a module can be determined only
by macro-expanding the module.
Instead of having the programmer declare import phases with
(for .... expand), etc., phases could be inferred
from uses of the imported identifiers. In that case,
determining the phase of an import would require macro-expanding
the module body.
The specification for library references should probably
include versioning. Without any change to the SRFI, we could say
that a version is part of a library's name in the universal
namespace, in which case every library reference selects a
specific version. A better system would allow specifications
such as "major version N, minor version of M or later", etc. In
this case, a
<lib-path> must be resolved to a
specific library name (including the version) based on the
This standard addresses the following specific goals:
It does not address the following:
5by itself as a program).
A library declaration contains the following elements:
A library definition is written with the
(library <lib-path> <language> <body>)
The syntax of
<body> depends on the
<language>. An implementation may allow a
reference to another library as a
this SRFI defines only the behavior when
"scheme://r6rs". Future standards may define the meaning of other
<language> forms, but it is expected that
will follow Scheme's lexical conventions, so that
read can process
(library "hello" "scheme://r6rs" (display "Hello World") (newline))
<body> is as follows:
<body> = <impexp-form>* <comdef-form>* <impexp-form> = (import <import-spec>*) | (export <export-spec>*) <comdef-form> = <command or definition> | (indirect-export <indirect-spec>*) | (begin <comdef-form>*) | <derived-comdef>
import form imports bindings into the library, and
export form exports bindings from the library. A
<command or definition> introduces a local
definition or executes an expression for its side-effects, and the
<command or definition> is the same as
in R5RS. No identifier can be imported multiple times, defined
multiple times, or both defined and imported. The
indirect-export form declares that certain macros might
expand to uses of certain unexported bindings.
<derived-comdef>is a macro call that expands into a
<import-spec> imports bindings for
use at a specific phase:
<import-spec> = <import-set> | (for <import-set> <import-phase>*) <import-phase> = run | expand
<import-set> imports for run
for form imports for either run time, expansion
time, or both. The listed
<export-spec> is merely an
<export-spec> = <export-set>
<indirect-spec> contains an identifier for a
macro defined in this library followed by a list of other identifiers
that are defined within this library:
<indirect-spec> = (<identifier> <identifier>*)
The meaning of an
<indirect-spec> is that the
expansion of a macro bound to the first identifier can contain
references to the remaining identifiers. If the first identifier is
exported from the library, then the other identifiers are potentially
accessible outside the library. If an identifier is not exported and
not indirectly exported with an exported macro, then accessing the
identifier from outside the library signals an error. (If access is
possible at all, then it is only possible through a macro whose
expansion is inconsistent with the declaration of indirect exports.) If
<identifier> in an
<indirect-spec> is not defined as a macro within
the library, or if any of the other
is not defined within the library, then an error is signalled.
<import-set> names a set of bindings from
another library, and gives them local names for the importing
<import-set> = <lib-path> | (only <X-set> <identifier>*) | (except <X-set> <identifier>*) | (add-prefix <X-set> <identifier>) | (rename <X-set> (<identifier> <identifier>)*)
<lib-path> names the exports of a
particular library. Initially, the internal names for the imports are the same
as the external names from the exporting library, and forms such as
rename adjust the internal
set of internal names. More specifically:
onlyform produces a subset of the bindings from another
<X-set>, including only the listed internal (external)
<identifier>s; if any of the included
<identifier>s is not in
<X-set>, an error is reported.
exceptform produces a subset of the bindings from another
<X-set>, including all but the listed internal (external)
<identifier>s; if any of the excluded
<identifier>s is not in
<X-set>, an error is reported.
add-prefixadds a prefix to each internal (external) name from another
renameform, for each pair of identifiers
(<identifier> <identifier>), removes a binding from the set from
<X-set>, and adds it back with a different internal (external) name. The first
identifieris the original internal (external) name, and the second
identifieris the new internal (external) name. If the original name is not in
<X-set>, or if the new name is already in
<X-set>, an error is reported.
<export-set> names a set of imported and locally
defined bindings, and gives them external names for exporting:
<export-set> = <identifier> | (rename (<identifier> <identifier>)*)
<identifier> names a single binding defined
within the library or imported, where the
external name for the export is the same as the name of
the binding within the library. A
rename set exports the binding
named by the first
<identifier> in each pair, using
<identifier> as the external
Examples for various |
(library "stack" "scheme://r6rs" (export make push! pop! empty!) (define (make) (list '())) (define (push! s v) (set-car! s (cons v (car s)))) (define (pop! s) (let ([v (caar s)]) (set-car! s (cdar s)) v)) (define (empty! s) (set-car! s '()))) (library "balloons" "scheme://r6rs" (export make push pop) (define (make w h) (cons w h)) (define (push b amt) (cons (- (car b) amt) (+ (cdr b) amt))) (define (pop b) (display "Boom! ") (display (* (car b) (cdr b))) (newline))) (library "party" "scheme://r6rs" (import (only "stack" make push! pop!) ; not empty! (add-prefix "balloons" balloon:)) ;; Total exports: make, push, ;; push!, ;; make-party, ;; pop!, pop-again! (export (rename (balloon:make make) (balloon:push push)) push! make-party (rename (party-pop! pop!))) ;; Creates a party as a stack of balloons, starting with ;; two balloons (define (make-party) (let ([s (make)]) ; from stack (push! s (balloon:make 10 10)) (push! s (balloon:make 12 9)) s)) (define (party-pop! p) (balloon:pop (pop! p)))) (library "main" "scheme://r6rs" (import "party") (define p (make-party)) (pop! p) ; displays "Boom! 108" (push! p (push (make 5 5) 1)) (pop! p)) ; displays "Boom! 24"
Examples for macros and phases:
(library "helper" "scheme://r6rs" (export find-dup) (define (find-dup l) (and (pair? l) (let loop ((rest (cdr l))) (cond [(null? rest) (find-dup (cdr l))] [(bound-identifier=? (car l) (car rest)) (car rest)] [else (loop (cdr rest))]))))) (library "let-values" "scheme://r6rs" (import (for "helper" expand)) (export let-values) (define-syntax let-values (lambda (stx) (syntax-case stx () [(_ [(id ...) expr] body0 body ...) (not (find-dup (syntax-object->list (syntax (id ...))))) (syntax (call-with-values (lambda () expr) (lambda (id ...) body0 body ...)))])))) (library "let-div" "scheme://r6rs" (import "let-values") (export let-div) (indirect-export (let-div quotient+remainder)) (define (quotient+remainder n d) ;; recompute, for now... (values (quotient n d) (remainder n d))) (define-syntax let-div (syntax-rules () [(_ n d (q r) body0 body ...) (let-values [(q r) (quotient+remainder n d)] body0 body ...)])))
import form, all imported bindings are
available for processing of
<comdef-form>s. Variables and macros imported into
the expand-time phase with
(for .... expand) are
available for use within macro definitions. More precisely, libraries
imported for run time must be visited (see Library Semantics) if they have not been visited
already, and libraries imported for expand time with
expand) must be invoked (see Library Semantics) if they have not been invoked
To discover macro definitions within the library,
<comdef-form>s are partially expanded first to
last; expansion proceeds only far enough to determine whether it is a
begin form, or something else. If the
<comdef-form> is a macro definition, then the macro
becomes available for use in expanding later
<comdef-form>s. If the
<comdef-form> is a
begin form, the
content forms are sliced into the library top-level in place of the
begin form, and partial expansion proceeds with the newly
After all macro definitions have been discovered, macro expansion
resumes for each
using all discovered macros. In this way, macros defined later can be
used to generate non-definition
used on the right-hand side of definition
<comdef-form>s. An error is signalled if expansion
<comdef-form> produces a definition in this
second phase (i.e., expansion requires a macro defined later in the
library to produce a definition form).
Macro expansion obeys lexical scope in the sense that if a macro expansion introduces a identifier that is bound in the context of the macro definition, it refers to the binding in the environment of the macro-definition library, not in the environment of the expansion.
An error is signalled if a macro expands to an
<impexp-form>; all imports and exports must be
apparent before macro expansion, and they must appear before any
in contrast, can appear in a macro expansion.
To avoid ambiguity, no local identifier can be imported or defined multiple times.
Since a library contains a sequence of definitions and expressions, a library can be used as an application. Alternately, a Scheme program standard (not part of this SRFI) may refer to libraries to be executed as part of the program. In either case, executing the library must imply executing any library that it imports.
More formally, we define library execution in terms of invoking libraries. To execute a library, invoke it at phase 0.
To invoke a library at phase N:
The order in which imported libraries are invoked is not defined, but imported libraries must be invoked before the library's definitions and top-level expressions are evaluated.
As noted in Library Macro Expansion, compiling a library implies visiting some libraries that it imports, as well as invoking others. Specifically, compiling a library requires the same action as visiting the library at phase 0.
To visit a library at phase N:
for .... expand, and that is not yet invoked at phase N+1.
The order in which imported libraries are visited and invoked is not defined, but imported libraries must be visited and invoked before the library's syntax definitions are evaluated.
An error is signalled if an identifier is used out of its declared phase(s).
A library refers to another library through a
<lib-path>. Conceptually, a
<lib-path> is a name within a universal
space. Library names in the universe are arranged in a tree, so that a
<lib-path> can be relative, much like a filesystem
path or URL.
<lib-path> is therefore represented by a
hierarchical URI string (see RFC 2396) or a symbol shortcut (defined
below). The URI scheme ``scheme'' should be used to refer to libraries
that are released by the Scheme community at large, in much the same
way that a Java package name is used. For example,
"scheme://acme.com/wiley/quicksort" might refer to a
quicksort library that is distributed by Wiley at Acme. If Wiley's
quicksort library contains the
"utils", it can be expanded as
"scheme://r6rs" must be supported by every
implementation, and it must export all bindings in R5RS, plus
indirect-export. URIs starting with
"scheme://srfi-N" are reserved for definition by
The way that URIs for library references are mapped to
library declarations is
implementation-specific. Implementations might, for example, interpret
a ``http'' URI as a reference to library within a UTF-8-encoded
package on the web. The resolution of a relative
<lib-path> for a library name (as in the examples
of Library Form Syntax) is also
implementation-dependent, but by convention, sequences of declarations
with relative names (again, as in ) are all relative to the same
(unspecified) absolute name.
This SRFI defines no interaction between
load procedures. Programmers
should not expect that an
appears with a
library body either references or extends
the definitions within the
This SRFI also does not require that implementations support a mode
library forms interactively. An
implementation's native module form might be completely different from
library, and the implementation may simply supply a tool
to translate packaged libraries into the implementation's native
A reference implementation for PLT Scheme 299.200 supports
library forms in the MzScheme REPL, and also a way to install a
library declarations into the usual PLT Scheme
module space (by splitting the
library sequence into
separate files, adding
#reader(lib "library.ss" "r6rs")
to the top of each file).
This SRFI was written in consultation with the full set of R6RS editors: Will Clinger, Kent Dybvig, Marc Feeley, Matthew Flatt, Manuel Serrano, Michael Sperber, and Anton van Straaten.
See www.readscheme.org for an extensive list of references to work on modules. The following are the most immediate influences on this SRFI:
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 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.