As discussed above, modules present constants, data variables, functions, and types in their interface. Their names can be the same as names in other modules or of local objects or types within a module that uses another. Name clashes are avoided because references to the entities presented by a module are qualified by the module type name or an object of that module type.
For example, after the module and variable declarations:
M : module { One : con 1; Thing : adt { t : int; f : fn(); }; g : fn(); }; m : M;the name
One
refers to the constant defined in the M
module only in the contexts M->One
or m->One
;
the name Thing
as the particular data type associated with the M
module can be referred to only in contexts like:
th1 : M->Thing; th2 : m->Thing;Finally, to call a function or object defined either as a top-level member of the module, or as a member of one of its
adt
s, it is necessary to declare, and also dynamically initialize using load
, a handle for the module. Then calls of the form:
m->g(); m->th1.f();become appropriate. It is possible to use just the type name of a module to qualify its constants and types because constants and types can be understood without having the code and data present. Calling a function or object declared by a module or one of its
adt
requires loading the module. The import
declaration:
identifier-list : import identifier;lifts the identifiers in the identifier-list into the scope in which
import
appears so that they are usable without a qualifier. The identifier after the import
keyword is either a module identifier, or an identifier declared as having that type. The initial list of identifiers specifies those constants, objects types, and functions of the module whose names are promoted. In the case of constants and types, import
merely makes their names accessible without using a qualifier. In the example above, if the module
declaration above had been followed by
One, Thing : import M;then one could refer to just
One
instead of M->One
; similarly an object could be declared like:
th : Thing;For functions, objects, and
adt
's with functions as members, import
must specify a module variable (as opposed to a module identifier). Each imported name is associated with the specified module variable, and the current value of this module variable controls which instance of the module will be called. For example, after:
g, Thing : import m;then:
g();is equivalent to:
m->g();and:
th : Thing; th.f();is equivalent to:
th : M->Thing; m->th.f();When the module declaration for the module being implemented is encountered, an implicit
import
of all the names of the module is executed. That is, given:
implement Mod; . . . Mod: module { . . . };the constants and types of
Mod
are accessed as if they had been imported. The functions and data objects declared in Mod
are imported as well, and refer dynamically to the current instance of the module being implemented.