5. Modules

Entity definitions are packaged within modules. The syntax and semantics of RSL modules are similar to that of the Modula-2 programming language [Wirth 85]. The semantics of modules, though not the syntax, are similar to packages in the Java programming language [Gosling 00]. The basic format of an RSL module is the following:

module name ;
[imports]
[exports]
[attribute-definitions ... ;]
[entity definition; |
formal definition; ] ...
end name
Module imports and exports optionally define inter-module name visibility. Attribute-definitions specify user-defined attributes, as described in Section 6 below. If any attribute definitions are present, they must appear before entity definitions. Entity-definitions are objects and operations, as described in Section 3. Formal-definitions are discussed in Sections 7 and 8.

In terms of packaging, a module defines a name scope within which all defined entities are visible. Any definition within a given module may reference any other entity defined within the same module. Unlike many programming languages, an entity definition does not need to lexically precede its reference(s) within a module.

Normally, entities defined in two different modules are mutually invisible. For example if object A is defined in module M1 and operation B is defined in module M2, the definition of B cannot reference A as an input or output. The use of import and export declarations extends the visibility of names between modules. The general format of an import declaration is the following:

from module-name import entity- name,...
and the format of export is:
export entity-name,...
The entity-name list may be the single keyword all, which means that all entities defined are imported or exported. In the case of the import declaration, the entity-list may be of the form
all except entity-name,...
which means that all entities except those listed are imported.

Consider the following example:

module A;
    export O1;
    object O1 is ... ;
    object O2 is ... ;
end A;

module B;
    from A import O1;
    operation Op1 is
        inputs: O1, ... ;        (* Legal reference to O1 *)
    end Op1
    operation Op2 is
        inputs: O2, ... ;        (* Illegal reference to O2 *)
end A;
The import declaration in B makes object O1 visible within B. Hence, the reference to O1 in Op1 is fine. Since object O2 is not explicitly imported into B, the reference to O2 in Op2 is illegal.

It should be noted that imports must be matched by corresponding exports. That is, a name cannot be import into one module without having been exported from another. Conversely, if a module exports one or more entities, each of these entities must be referenced by at least one import.

The use of import/export can lead to name conflicts if a module both imports and defines an entity of the same name. For example:

module A;
    export O1;
    object O1 is ... ;
end A;

module B
    from A import O1;

    object O1 is ... ;        (* Name conflict *)
    operation Op is
        inputs: O1;           (* Ambiguous reference *)
Here module B both imports and defines O1. To overcome such name conflicts, names can be imported in qualified form, and referenced by prefixing with the name of the defining module. Qualified imports are achieved by importing an entire module, without the use of the from clause. The following is a version of the immediately preceding example with the name conflict removed:
module A;
    export O1;
    object O1 is ... ;
end A;

module B
    import A;

    object O1 is ... ;        (* No conflict *)
    operation Op is
        inputs: O1;           (* Legal reference to B's O1 *)
        outputs: A.O1;        (* Legal reference to A's O1 *)
end B;
Here, reference to the imported version of O1 is denoted by the qualified reference "A.O1" within B. The unqualified reference to O1 refers to the O1 defined within B. Hence, there is no name conflict in this case, since both versions of O1 can be referenced unambiguously.

When a module contains many definitions that all need to be exported, the all form of import/export is convenient. For example,

module A;
    export all;
    object O1 is ... ;
    ...
    object O100 is ... ;
end A;

module B
    from A import all;
    ...
end B;
The difference between importing an entire module by name versus importing all of its entities is a matter of qualification. That is, the import declaration
import A
makes all exports of A available only in qualified form. The import
from A import all
makes all exports of A available in unqualified form.

Whenever a module name appears in an import declaration, either in the from clause or in the entity name list, all of its exports are accessible in qualified form. For example,

module A
    export all;
    object O1 is ... ;
    ...
    object O100 is ... ;
end A;

module B
    from A import O1, O2, O3;
    ...
end B;
In this case, objects O1, O2, and O3 are accessible in unqualified form within module B. Objects O4 through O100 are accessible in qualified form within B. For example, A.O56 is a valid reference to object O56 from module A. Unqualified imports are also available in qualified form, for example both O2 and A.O2 are valid references within module B. However, there is typically no reason to reference unqualified imports in qualified form, unless there is a desire to emphasize that a particular entity comes from a particular module.

As explained above, unqualified imports will conflict with definitions of the same name in the importing module. There are two ways to deal with such potential conflicts. If an exporting module only needs to make visible some of its definitions, then selectively listing those in the export declaration can avoid conflicts with other modules that import all if its definitions. Alternatively, if a module exports all of its definitions, an importing module can import all but a selected list of the exports to avoid conflicts. Consider this example:

module A;
    export all;
    object O1 is ... ;
    ...
    object O56 is ... ;
    ...
    object O100 is ... ;
end A;

module B
    from A import all except O56;
    object O56 is ... ;
    ...
end B;
In this case, module B needs all of module A's exports, except for object O56. To avoid conflict with this object, the import declaration includes the all except clause, which causes O56 to be excluded from the list of unqualified imports. In this way, the definition of O56 in module B does not conflict with that in module A. The version of O56 from A is still available in module B in qualified form, i.e., as A.O56.




Prev: inheritance | Next: user-attrs | Up: index | Top: index