Next: , Up: Module system


3.1 Module system architecture

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.


Footnotes

[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.