Next: Macros in concert with modules, Previous: Module system architecture, Up: Module system
Scheme48's module system is used through a module configuration
language. The configuration language is entirely separate from
Scheme. Typically, in one configuration, or set of components that
compose a program, there is an interfaces.scm file that defines
all of the interfaces used by the configuration, and there is also a
packages.scm file that defines all of the packages & structures
that compose it. Note that modules are not necessarily divided into
files or restricted to one file: modules may include arbitrarily many
files, and modules' code may also be written in-line to structure
expressions (see the begin
package clause below), although that
is usually only for expository purposes and trivial modules.
Structures are always created with corresponding package clauses. Each clause specifies an attribute of the package that underlies the structure or structures created using the clauses. There are several different types of clauses:
Open
specifies that the package should open each of the listed structures, whose packages will be loaded if necessary.Access
specifies that each listed structure should be accessible using the(structure-ref
structure identifier)
special form, which evaluates to the value of identifier exported by the accessed structure structure.Structure-ref
is available from thestructure-refs
structure. Each structure passed toaccess
is not opened, however; the bindings exported thereby are available only usingstructure-ref
. While the qualifiedstructure-ref
mechanism is no longer useful in the presence of modified structures (see below onmodify
,subset
, &with-prefix
), some old code still uses it, andaccess
is also useful to force that the listed structures' packages be loaded without cluttering the namespace of the package whose clauses theaccess
clause is among.
Specifies a set of package clauses for the next floor of the reflective tower; see Macros in concert with modules.
Files
andbegin
specify the package's code.Files
takes a sequence of namelists for the filenames of files that contain code; see Filenames.Begin
accepts in-line program code.
Optimize
clauses request that specified compiler optimizers be applied to the code. (Actually, `optimizer' is a misnomer. Theoptimize
clause may specify arbitrary passes that the compiler can be extended with.)Integrate
clauses specify whether or not integrable procedures from other modules, most notably Scheme primitives such ascar
orvector-ref
, should actually be integrated in this package. This is by default on. Most modules should leave it on for any reasonable performance; only a select few, into which code is intended to be dynamically loaded frequently and in which redefinition of imported procedures is common, need turn this off. The value of the argument tointegrate
clauses should be a literal boolean, i.e.#t
or#f
; if no argument is supplied, integration is enabled by default.Currently, the only optimizer built-in to Scheme48 is the automatic procedure integrator, or
auto-integrate
, which attempts stronger type reconstruction than is attempted with most code (see Static type system) and selects procedures below a certain size to be made integrable (so that the body will be compiled in-line in all known call sites). Older versions of Scheme48 also provided another optimizer,flat-environments
, which would flatten certain lexical closure environments, rather than using a nested environment structure. Now, however, Scheme48's byte code compiler always flattens environments; specifyingflat-environments
in anoptimize
clause does nothing.
A configuration is a sequence of definitions. There are definition forms for only structures and interfaces.
Define-structure
creates a package with the given package clauses and defines name to be the single view atop it, with the interface interface.Define-structure
also creates a package with the given package clauses; upon that package, it defines each name to be a view on it with the corresponding interface.
Define-module
defines name to be a parameterized module that accepts the given parameters.
Defines name to be the interface that interface evaluates to. Interface may either be an interface constructor application or simply a name defined to be an interface by some prior
define-interface
form.
Export
constructs a simple interface with the given export specifiers. The export specifiers specify names to export and their corresponding static types. Each export-specifier should have one of the following forms:
- symbol
- in which case symbol is exported with the most general value type;
(
symbol type)
- in which case symbol is exported with the given type; or
((
symbol...)
type)
- in which case each symbol is exported with the same given type
For details on the valid forms of type, see Static type system. Note: All macros listed in interfaces must be explicitly annotated with the type
:syntax
; otherwise they would be exported with a Scheme value type, which would confuse the compiler, because it would not realize that they are macros: it would instead treat them as ordinary variables that have regular run-time values.
This constructs an interface that contains all of the export specifiers from each interface.
Structures may also be constructed anonymously; this is typically most useful in passing them to or returning them from parameterized modules.
Structure
creates a package with the given clauses and evaluates to a structure over it with the given interface.Structures
does similarly, but it evaluates to a number of structures, each with the corresponding interface.
These modify the interface of structure.
Subset
evaluates to a structure that exports only name ..., excluding any other names that structure exported.With-prefix
adds a prefix name to every name listed in structure's interface. Bothsubset
andwith-prefix
are syntactic sugar for the more generalmodify
, which applies the modifier commands in a strictly right-to-left or last-to-first order. Note: These all denote new structures with new interfaces; they do not destructively modify existing structures' interfaces.
Prefix
adds the prefix name to every exported name in the structure's interface.Expose
exposes only name ...; any other names are hidden.Hide
hides name ....Alias
exports each to as though it were the corresponding from, as well as each from.Rename
exports each to as if it were the corresponding from, but it also hides the corresponding from.Examples:
(modify structure (prefix foo:) (expose bar baz quux))makes only
foo:bar
,foo:baz
, andfoo:quux
, available.(modify structure (hide baz:quux) (prefix baz:) (rename (foo bar) (mumble frotz)) (alias (gargle mumph)))exports
baz:gargle
as what was originallymumble
,baz:mumph
as an alias for what was originallygargle
,baz:frotz
as what was originallymumble
,baz:bar
as what was originallyfoo
, notbaz:quux
— what was originally simplyquux
—, and everything else that structure exported, but with a prefix ofbaz:
.
There are several simple utilities for binding variables to structures
locally and returning multiple structures not necessarily over the same
package (i.e. not with structures
). These are all valid in the
bodies of define-module
and def
forms, and in the
arguments to parameterized modules and open
package clauses.
These are all as in ordinary Scheme. Note, however, that there is no reasonable way by which to use
values
except to call it, so it is considered a syntax; also note thatreceive
may not receive a variable number of values — i.e. there are no `rest lists' —, because list values in the configuration language are nonsensical.
Finally, the configuration language also supports syntactic extensions, or macros, as in Scheme.