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 defmodule
syntax


10.1.1.1 Syntax

defmodule-0-form:
( defmodule module-name
module-directives
level-0-module-form* )
module-name:
identifier
module-directives:
( module-directive* )
module-directive:
export ( identifier* )
expose ( module-descriptor* )
import ( module-descriptor* )
syntax ( module-descriptor* )
level-0-module-form:
( export identifier* )
level-0-form
defining-0-form
( progn level-0-module-form* )
module-descriptor:
module-name
module-filter
module-filter:
( except ( identifier* ) module-descriptor )
( only ( identifier* ) module-descriptor )
( rename ( rename-pair* ) module-descriptor )
rename-pair:
( identifier identifier )
level-0-form:
identifier
literal
special-0-form
function-call-form
form:
level-0-form
special-form:
special-0-form
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:
defining-0-form:
defclass-form
defcondition-form
defconstant-form
deflocal-form
defgeneric-form
defsyntax-form
defun-form

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!

special-0-form:
defmethod-form
generic-lambda-form
quote-form
lambda-form
setq-form
if-form
let/cc-form
letfuns-form
progn-form
unwind-protect-form
quasiquote-form
unquote-form
unquote-splicing-form
call-next-handler-form
with-handler-form
cond-form
and-form
or-form
block-form
return-from-form
let-form
let-star-form
with-input-file-form
with-output-file-form
with-source-form
with-sink-form

10.5 Module Processing

The following steps summarize the module definition process:
directive processing:
This is described in detail in § 10.210.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.