The Semantics of Environments and Modules¶
This section describes the semantics of modules.
The following sections provide a sketchy description of the semantics of modules.
Modules are bases on so called environments.
Environments fulfill basically the same purpose
as Smalltalk the single instance of
They bind names that objects. Nearly all of the objects bound
in an Environment are classes. All classes in an environment
are immediately available to the code in all other classes
bound the same environment by just mentioning their name.
Obviously classes defined in an environment can be used
by classes outside their environment only with some hassle.
Modules however provide means to make bound names available to other modules by exporting them. Exported symbols can be grouped into so called Interfaces. Otherwise Modules are just environments.
Interfaces can be imported by other modules.
When a module is created it starts out with two Interfaces:
- API The Application Programming Interface
This interface should contain all the classes that are useful to the module’s clients, that is to classes that want use (instances of) the modules classes.
- SPI The System Programming Interface
This interface is created as an alias of the API 1. It should contain all classes another module or class needs to enhance the functionality of the exporting module. Usually this is done by sub-classing these classes.
Some of Haver’s own modules defined so called UTIs (Unit Test Interfaces) that export classes which are not exported by the SPI. This should make white-box testing easier.
Of course one can name a module’s interfaces as one pleases.
Modules can import an interface by creating an Import Specification. Import Specifications can import an interface of another module as a whole or they can explicitly select symbols of an interface. It also possible to specify, that you want to import all symbols of an interface and exclude it some of the exported symbols.
It is possible to import an interface more than once into a module.
Symbols can be renamed upon export and import.
Each module, interface, exported symbol, import specification and imported symbol has an associated package, denoted by the package’s name. The default package name is the module name (of the interface, exported symbol, import …).
The export an import definitions are stored in the package-file of the package denoted by the package name.
This makes it possible to specify a different package name for a Unit Testing Interface UTI and store the definition of this interface in a special unit test package.
In the following section I try to give a formal definition for the semantics of modules and environments.
The set of “Environment Identifiers” is a finite set of arbitrary Smalltalk objects.
In essence symbols are immutable strings.
Symbols come in two variants:
“Interned Symbols” are those symbols known to the symbol class. Two interned symbols are identical wrt. to == when their character strings are equal wrt. =. This code snipped may serve as an explanation:('one', 'two') asSymbol == 'onetwo' asSymbol
evaluates to true 2. Especially:Symbol lookup: 'onetwo'
and:Symbol lookup: 'one', 'two'
should evaluate to the same symbol: #onetwo
All other symbols are uninterned.
All symbols in Haver respond to the message environment. All interned symbols answer nil when the message environment is sent no them.
Local symbols answer an environment identifier when the message environment is sent to them. This environment is not nil.
It follows from above propositions, that all local symbols are uninterned symbols.
“Environments” are mappings from local symbols to arbitrary Smalltalk objects. All symbols in the domain of the aforementioned mapping answer the environment when sent the message environment.
A local symbol can be created from any symbol by sending the message #forEnvironment: to it. The message needs an environment identifier as an argument.
A bound symbol is a symbol that is the domain of an environment’s mapping.
“Modules” are environments associated with two other sets, A set of interfaces and a set of import specifications.
An export specification is a mapping from a symbols to symbols.
Invalid Export Specifications
An “invalid export specification” is an export specification whose domain symbol is not bound in the module.
An interface is a set of export specifications.
Symbol Import Specification
An “symbol import specification” is mapping from an export specification to a symbol.
An import specification is a pair consisting of an interface and a set of symbol import specifications.
Invalid Import Specification
An “invalid import specification” is an “import specification” if its interface is not a member of any module’s set of interfaces.
Invalid Symbol Import Specification
An “invalid symbol import specification” is a symbol import specification if its export specification is not a member of any module’s interface or if its export specification is invalid.
The mappings in export specifications and symbol import specifications provide a means to rename a symbol on export and on import.
If the compiler uses #bindingOf: to find the binding of a global variable the binding is searched first in the environments then in Smalltalk.
In the case of an environment the binding only searched in the bindings of an environment 3.
In the case of a module the same is done. If this yields no binding, all valid symbol import specifications are searched for a matching name and the association in the exporting module is answered.
There is no UI-support to explicitly create interfaces that are aliases for other interfaces. Likewise new interfaces can not be created as an alias.
This should be true for every Smalltalk implementation.
Of course users may implement their own environment managers with vastly different semantics.