Next: Module configuration language, Up: Module system
The fundamental mechanism by which Scheme code is evaluated is the lexical environment. Scheme48's module system revolves around this fundamental concept. Its purpose is to control the denotation of names in code1 in a structured, modular manner. The module system is manipulated by a static configuration language, described in the next section; this section describes the concepts in the architecture of the module system.
The package is the entity internal to the module system that
maps a set of names to denotations. For example, the package that
represents the Scheme language maps lambda
to a descriptor for
the special form that the compiler interprets to construct a procedure,
car
to the procedure that accesses the car of a pair, &c.
Packages are not explicitly manipulated by the configuration language,
but they lie underneath structures, which are described below. A
package also contains the code of a module and controls the visibility
of names within that code. It also includes some further information,
such as optimizer switches. A structure is a view on a package;
that is, it contains a package and an interface that lists all of
the names it exports to the outside. Multiple structures may be
constructed atop a single package; this mechanism is often used to offer
multiple abstraction levels to the outside. A module is an
abstract entity: it consists of some code, the namespace visible to the
code, and the set of abstractions or views upon that code.
A package contains a list of the structures whose bindings should be available in the code of that package. If a structure is referred to in a such a list of a package, the package is said to open that structure. It is illegal for a package to open two structures whose interfaces contain the same name.2 Packages may also modify the names of the bindings that they import. They may import only selected bindings, exclude certain bindings from structures, rename imported bindings, create alias bindings, and add prefixes to names.
Most packages will open the standard scheme
structure, although
it is not implicitly opened, and the module system allows not opening
scheme
. It may seem to be not very useful to not open it, but
this is necessary if some bindings from it are intended to be shadowed
by another structure, and it allows for entirely different languages
from Scheme to be used in a package's code. For example, Scheme48's
byte code interpreter virtual machine is implemented in a subset of
Scheme called Pre-Scheme, which is described in a later chapter in this
manual. The modules that compose the VM all open not the scheme
structure but the prescheme
structure. The configuration
language itself is controlled by the module system, too. In another
example, from Scsh, the Scheme shell, there is a structure scsh
that contains all of the Unix shell programming facilities. However,
the scsh
structure necessarily modifies some of the bindings
related to I/O that the scheme
structure exports. Modules could
not open both scheme
and scsh
, because they both provide
several bindings with the same names, so Scsh defines a more convenient
scheme-with-scsh
structure that opens both scheme
, but
with all of the shadowed bindings excluded, and scsh
; modules
that use Scsh would open neither scsh
nor scheme
: they
instead open just scheme-with-scsh
.
Interfaces are separated from structures in order that they may be
reüsed and combined. For example, several different modules may
implement the same abstractions differently. The structures that they
include would, in such cases, reüse the same interfaces. Also, it is
sometimes desirable to combine several interfaces into a compound
interface; see the compound-interface
form in the next section.
Furthermore, during interactive development, interface definitions may
be reloaded, and the structures that use them will automatically begin
using the new interfaces; see Using the module system.
Scheme48's module system also supports parameterized modules. Parameterized modules, sometimes known as generic modules, higher-order modules or functors, are essentially functions at the module system level that map structures to structures. They may be instantiated or applied arbitrarily many times, and they may accept and return arbitrarily many structures. Parameterized modules may also accept and return other parameterized modules.
[1] This is in contrast to, for example, Common Lisp's package system, which controls the mapping from strings to names.
[2] The current implementation,
however, does not detect this. Instead it uses the left-most structure
in the list of a package's open
clause; see the next section for
details on this.