Next: , Previous: Module system architecture, Up: Module system


3.2 Module configuration language

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:

— package clause: open structure ...
— package clause: access structure ...

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 the structure-refs structure. Each structure passed to access is not opened, however; the bindings exported thereby are available only using structure-ref. While the qualified structure-ref mechanism is no longer useful in the presence of modified structures (see below on modify, subset, & with-prefix), some old code still uses it, and access is also useful to force that the listed structures' packages be loaded without cluttering the namespace of the package whose clauses the access clause is among.

— package clause: for-syntax package-clause ...

Specifies a set of package clauses for the next floor of the reflective tower; see Macros in concert with modules.

— package clause: files file-specifier ...
— package clause: begin code ...

Files and begin 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.

— package clause: optimize optimizer-specifier ...
— package clause: integrate [on?]

Optimize clauses request that specified compiler optimizers be applied to the code. (Actually, `optimizer' is a misnomer. The optimize 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 as car or vector-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 to integrate 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; specifying flat-environments in an optimize clause does nothing.

A configuration is a sequence of definitions. There are definition forms for only structures and interfaces.

— configuration form: define-structure name interface package-clause ...
— configuration form: define-structures ((name interface) ...) package-clause ...

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.

— configuration form: define-module (name parameter ...) definition ... result
— configuration form: def name ... (parameterized-module argument ...)

Define-module defines name to be a parameterized module that accepts the given parameters.

— configuration form: define-interface name interface

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.

— interface constructor: export export-specifier ...

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.

— interface constructor: compound-interface interface ...

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 constructor: structure interface package-clauses
— structure constructor: structures (interface ...) package-clauses

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.

— structure constructor: subset structure (name ...)
— structure constructor: with-prefix structure name
— structure constructor: modify structure modifier ...

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. Both subset and with-prefix are syntactic sugar for the more general modify, 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.

— modifier command: prefix name
— modifier command: expose name ...
— modifier command: hide name ...
— modifier command: alias (from to) ...
— modifier command: rename (from to) ...

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, and foo:quux, available.

          (modify structure
                  (hide baz:quux)
                  (prefix baz:)
                  (rename (foo bar)
                          (mumble frotz))
                  (alias (gargle mumph)))

exports baz:gargle as what was originally mumble, baz:mumph as an alias for what was originally gargle, baz:frotz as what was originally mumble, baz:bar as what was originally foo, not baz:quux — what was originally simply quux —, and everything else that structure exported, but with a prefix of baz:.

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.

— syntax: begin body
— syntax: let ((name value) ...) body
— syntax: receive (name ...) producer body
— syntax: values value ...

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 that receive 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.

— configuration form: define-syntax name transformer-specifier

Defines the syntax transformer name to be the transformer specified by transformer-specifier. Transformer-specifier is exactly the same as in Scheme code; it is evaluated as ordinary Scheme.