10 Modules
The EULISP module scheme has several influences: LeLisp’s
module system and module compiler (complice), Haskell, ML [13],
MIT-Scheme’s make-environment and T’s locales.
All bindings of objects in EULISP reside in some module
somewhere. Also, all programs in EULISP are written as one or
more modules. Almost every module imports a number of other
modules to make its definition meaningful. These imports have
two purposes, which are separated in EULISP: firstly the
bindings needed to process the syntax in which the module is
written, and secondly the bindings needed to resolve the
free variables in the module after syntax expansion. These
bindings are made accessible by specifying which modules
are to be imported for which purpose in a directive at the
beginning of each module. The names of modules are bound in a
disjoint binding environment which is only accessible via
the module definition form. That is to say, modules are not
first-class. The body of a module definition comprises a list
of directives followed by a sequence of level-0 and export
forms.
The module mechanism provides abstraction and security in a form
complementary to that provided by the object system. Indeed,
although objects do support data abstraction, they do not support
all forms of information hiding and they are usually conceptually
smaller units than modules. A module defines a mapping between
a set of names and either local or imported bindings of those
names. Most such bindings are immutable. The exception are
those bindings created by deflocal which may be modified
by both the defining and importing modules. There are no
implicit imports into a module—not even the special forms are
available in a module that imports nothing. A module exports
nothing by default. Mutually referential modules are not
possible because a module must be defined before it can be
used. Hence, the importation dependencies form a directed
acyclic graph. The processing of a module definition uses
three environments, which are initially empty. These are the
top-lexical, the external and the syntax environments of the
module.
-
top-lexical:
- The top-lexical environment comprises all
the locally defined bindings and all the imported bindings.
-
external:
- The external environment comprises all the
exposed bindings—bindings from modules being exposed
by this module but not necessarily imported—and all the
exported bindings, which are either local or imported.
Thus, the external environment might not be a subset
of the top-lexical environment because, by virtue of an
expose directive, it can contain bindings from modules
which have not been imported. This is the environment
received by any module importing this module.
-
syntax:
- The syntax environment comprises all the
bindings available for the syntax expansion of the module.
Each binding is a pair of a local-name and a module-name. It is a
violation if any two instances of local-name in any one of these
environments have different module-names. This is called a name
clash. These environments do not all need to exist at the same
time, but it is simpler for the purposes of definition to describe
module processing as if they do.
10.1 Module Definition
10.1.1.1 Syntax
Arguments
-
module name :
-
A symbol used to name the module.
-
module directives :
-
A form specifying the exported names, exposed modules,
imported modules and syntax modules used by this
module.
-
module form :
-
One of a defining form, an expression or an export
directive.
Remarks
The defmodule form defines a module named by module-name and
associates the name module-name with a module object in the
module binding environment.
NOTE 1 Intentionally, nothing is defined about any relationship
between modules and files.
Examples
An example module definition with explanatory comments is given
in example 1.
10.2 Directives
The list of module directives is a sequence of keywords and forms,
where the keywords indicate the interpretation of the forms (see
syntax table 10.1.1.1). This representation allows for the addition
of further keywords at other levels of the definition and also for
implementation-defined keywords. For the keywords given
here, there is no defined order of appearance, nor is there
any restriction on the number of times that a keyword can
appear. Multiple occurrences of any of the directives defined
here are treated as if there is a single directive whose form is
the combination of each of the occurrences. This definition
describes the processing of four keywords, which are now
described in detail. The syntax of all the directives is given in
syntax table 10.1.1.1 and an example of their use appears in
example 1.
Example 1: | module directives |
(defmodule a-module
(import
(module-1 ;; import everything from module-1
(except (binding-a) module-2) ;; all but binding-a from module-2
(only (binding-b) module-3) ;; only binding-b from module-3
(rename
((binding-c binding-d) (binding-d binding-c)) ;; all of module-4, but exchange
module-4)) ;; the names of binding-c and binding-d
syntax
(syntax-module-1 ;; all of the module syntax-module-1
(rename ((syntax-a syntax-b)) ;; rename the binding of syntax-a
syntax-module-2) ;; of syntax-module-2 as syntax-b
(rename ((syntax-c syntax-a)) ;; rename the binding of syntax-c
syntax-module-3)) ;; of syntax-module-3 as syntax-a
expose
((except (binding-e) module-5) ;; all but binding-e from module-5
module-6) ;; export all of module-6
export
(binding-1 binding-2 binding-3)) ;; and three bindings from this module
...
(export local-binding-4) ;; a fourth binding from this module
...
(export binding-c) ;; the imported binding binding-c
...)
10.2.1 export Directive
This is denoted by the keyword export followed by a sequence of
names of top-lexical bindings—these could be either locally-defined
or imported—and has the effect of making those bindings
accessible to any module importing this module by adding them to
the external environment of the module. A name clash can arise
in the external environment from interaction with exposed
modules.
10.2.2 import Directive
This is denoted by the keyword import followed by a sequence of
module-descriptors (see syntax table 10.1.1.1), being module
names or the filters except, only and rename. This sequence
denotes the union of all the names generated by each element of
the sequence. A filter can, in turn, be applied to a sequence of
module descriptors, and so the effect of different kinds of filters can
be combined by nesting them. An import directive specifies either
the importation of a module in its entirety or the selective
importation of specified bindings from a module.
The purpose of this directive is to specify the imported bindings
which constitute part of the top-lexical environment of a
module. These are the explicit run-time dependencies of the
module. Additional run-time dependencies may arise as a
result of syntax expansion. These are called implicit run-time
dependencies.
In processing import directives, every name should be thought of
as a pair of a module-0-name and a local-name. Intuitively, a
namelist of such pairs is generated by reference to the module
name and then filtered by except, only and rename. In an import
directive, when a namelist has been filtered, the names are
regarded as being defined in the top-lexical environment of the
module into which they have been imported. A name clash can
arise in the top-lexical environment from interaction between
different imported modules. Elements of an import directive are
interpreted as follows:
-
module-name:
- All the exported names from module-name.
-
except :
- Filters the names from each module-descriptor
discarding those specified and keeping all other names.
The except directive is convenient when almost all of
the names exported by a module are required, since it is
only necessary to name those few that are not wanted to
exclude them.
-
only :
- Filters the names from each module-descriptor
keeping only those names specified and discarding all other
names. The only directive is convenient when only a few
names exported by a module are required, since it is only
necessary to name those that are wanted to include them.
-
rename :
- Filters the names from each module-descriptor
replacing those with old-name by new-name. Any name
not mentioned in the replacement list is passed unchanged.
Note that once a name has been replaced the new-name
is not compared against the replacement list again. Thus,
a binding can only be renamed once by a single rename
directive. In consequence, name exchanges are possible.
10.2.3 expose Directive
This is denoted by the keyword expose followed by a list of
module-directives (see syntax table 10.1.1.1). The purpose of this
directive is to allow a module to export subsets of the external
environments of various modules without importing them itself.
Processing an expose directive employs the same model as for
imports, namely, a pair of a module-name and a local-name with
the same filtering operations. When the namelist has been
filtered, the names are added to the external environment
of the module begin processed. A name clash can arise in
the external environment from interaction with exports or
between different exposed modules. As an example of the use of
expose, a possible implementation of the level-0 module
is shown in example 1.
Example 1: | module using
expose |
(defmodule level-0
(expose
(character collection compare condition convert
copy double-float elementary-functions event
formatted-io int function
keyword list lock number object-0 stream string
symbol syntax-0 table thread vector)))
It is also meaningful for a module to include itself in an expose
directive. In this way, it is possible to refer to all the bindings in
the module being defined. This is convenient, in combination with
except (see § 10.2.2), as a way of exporting all but a few
bindings in a module, especially if syntax expansion creates
additional bindings whose names are not known, but should be
exported.
10.2.4 syntax Directive
This directive is processed in the same way as an import directive,
except that the bindings are added to the syntax environment.
This environment is used in the second phase of module
processing (syntax expansion). These constitute the dependencies
for the syntax expansion of the definitions and expressions
in the body of the module. A name clash can arise in the
syntax environment from interaction between different syntax
modules.
It is important to note that special forms are considered part of the
syntax and they may also be renamed.
10.3 Definitions and Expressions
Definitions in a module only contain unqualified names—that is,
local-names, using the above terminology and are created by defining
forms:
A top-lexical binding is created exactly once and shared with all
modules that import its exported name from the module that
created the binding. A name clash can arise in the top-lexical
environment from interaction between local definitions and
between local definitions and imported modules. Only top-lexical
bindings created by deflocal are mutable—both in the defining
module and in any importing module. It is a violation to modify an
immutable binding. Expressions, that is non-defining forms, are
collected and evaluated in order of appearance at the end of the
module definition process when the top-lexical environment is
complete—that is after the creation and initialization of the
top-lexical bindings. The exception to this is the progn form, which
is descended and the forms within it are treated as if the progn
were not present. Definitions may only appear either at top-level
within a module definition or inside any number of progn forms.
This is specified more precisely in the grammar for a module in
syntax table 10.1.1.1.
10.4 Special Forms
***HGW Say something!
10.5 Module Processing
The following steps summarize the module definition process:
-
directive processing:
- This is described in detail in
§ 10.2–10.2.4. This step creates and initializes the
top-lexical, syntax and external environments.
- syntax expansion:
- The body of the module is expanded
according to the operators defined in the syntax environment
constructed from the syntax directive.
NOTE 1 The semantics of syntax expansion are still under
discussion and will be described fully in a future version of the
EULISP definition. In outline, however, it is intended that the
mechanism should provide for hygenic expansion of forms in
such a way that the programmer need have no knowledge of the
expansion-time or run-time dependencies of the syntax defining
module. Currently syntax expansion is unhygienic to allow a
simple syntax for syntax operator definition.
-
static analysis:
- The expanded body of the module is
analyzed. Names referenced in export forms are added to
the external environment. Names defined by defining
forms are added to the top-lexical environment. It is a
violation, if a free identifier in an expression or defining form
does not have a binding in the top-lexical environment.
NOTE 2 Additional implementation-defined steps may be
added here, such as compilation.
-
initialization:
- The top-lexical bindings of the module
(created above) are initialized by evaluating the defining forms
in the body of the module in the order they appear.
NOTE 3 In this sense, a module can be regarded as a
generalization of the letfuns form of this and other Lisp
dialects.
-
expression evaluation:
- The expressions in the body of
the module are evaluated in the order in which they
appear.