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:
Openspecifies that the package should open each of the listed structures, whose packages will be loaded if necessary.Accessspecifies that each listed structure should be accessible using the(structure-refstructure identifier)special form, which evaluates to the value of identifier exported by the accessed structure structure.Structure-refis available from thestructure-refsstructure. Each structure passed toaccessis not opened, however; the bindings exported thereby are available only usingstructure-ref. While the qualifiedstructure-refmechanism is no longer useful in the presence of modified structures (see below onmodify,subset, &with-prefix), some old code still uses it, andaccessis also useful to force that the listed structures' packages be loaded without cluttering the namespace of the package whose clauses theaccessclause is among.
Specifies a set of package clauses for the next floor of the reflective tower; see Macros in concert with modules.
Filesandbeginspecify the package's code.Filestakes a sequence of namelists for the filenames of files that contain code; see Filenames.Beginaccepts in-line program code.
Optimizeclauses request that specified compiler optimizers be applied to the code. (Actually, `optimizer' is a misnomer. Theoptimizeclause may specify arbitrary passes that the compiler can be extended with.)Integrateclauses specify whether or not integrable procedures from other modules, most notably Scheme primitives such ascarorvector-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 tointegrateclauses should be a literal boolean, i.e.#tor#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-environmentsin anoptimizeclause does nothing.
A configuration is a sequence of definitions. There are definition forms for only structures and interfaces.
Define-structurecreates a package with the given package clauses and defines name to be the single view atop it, with the interface interface.Define-structurealso 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-moduledefines 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-interfaceform.
Exportconstructs 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.
Structurecreates a package with the given clauses and evaluates to a structure over it with the given interface.Structuresdoes similarly, but it evaluates to a number of structures, each with the corresponding interface.
These modify the interface of structure.
Subsetevaluates to a structure that exports only name ..., excluding any other names that structure exported.With-prefixadds a prefix name to every name listed in structure's interface. Bothsubsetandwith-prefixare 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.
Prefixadds the prefix name to every exported name in the structure's interface.Exposeexposes only name ...; any other names are hidden.Hidehides name ....Aliasexports each to as though it were the corresponding from, as well as each from.Renameexports 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:gargleas what was originallymumble,baz:mumphas an alias for what was originallygargle,baz:frotzas what was originallymumble,baz:baras 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
valuesexcept to call it, so it is considered a syntax; also note thatreceivemay 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.