📜 ⬆️ ⬇️

Oberon is dead, long live Oberon! Part 2. Modules

There are a lot of publications and discussions about necessity / uselessness, merits / demerits of the concept of modules in programming languages, so I’ll just talk about the implementation of the system of modules in Oberon-family languages.

A module in Oberon is not only a unit of compilation, loading and linking, it is also an encapsulation mechanism. When referring to entities of a connected (imported) module, a mandatory qualification of this module is required. For example, if module A imports module B , and uses its variable v , then access to this variable should be of the form Bv , which reduces the number of difficult to trace errors using completely different entities with the same name in non-modular languages ​​depending on the sequence of connecting files and compiler behavior .

As I already said, the Oberon encapsulation is also built on the concept of a module — all types declared in the module are transparent to each other, and external clients access to the module's entities through access specifiers. Currently the following access specifiers are available in Active Oberon:
* "full access" specifier - the identifier is marked with an asterisk (*);
* qualifier "read-only access" - the identifier is marked with a minus sign (-);
Identifiers with missing qualifiers are not available to external clients.
For example:
TYPE Example* = RECORD a*, b-, c : LONGINT; END; 

Describes the type of the Example1 entry exported outside the module. The a field is readable and writable to the clients of the module in which the type is declared, the b field is read-only and the c field is hidden from external clients.

The name of the module (the final object file), indicated after the MODULE keyword, may not coincide with the file name, which is used, for example, to separate module implementations. This mechanism can be used instead of the conditional compilation directive mechanism - we always connect a module with a known and fixed name, and the build tools generate the necessary module according to the assembly conditions — various operating systems, processors, etc.
')
In the Active Oberon, plug-in (imported) modules can have aliases, different modules can have the same alias, forming a kind of namespace or pseudo-module, the namespace entities are addressed by its name, the real module names are not available. Names of entities that are in such a namespace should not intersect.

Historically, as usual, the Oberon operating environment provides an interface for dynamically loading and unloading modules, although static linking is also possible, as, for example, in Pow! or OO2C. The language itself provides only the module import section and the module initialization section. In some implementations, there is also a module finalization section, but, in general, the runtime environment provides the programmer with an interface for registering finalizer procedures that are automatically called when a module is unloaded or when the program is closed during static binding.
Typical module structure in Active Oberon:

 module Name; import Modules, ....; procedure Finalize*; begin ... end Finalize; begin (*   *) Modules.InstallTermHandler(Finalize); (*    *) ... end Name. 

(Yes, in Active Oberon, keywords can be in lower case, and not just KAPS, the set is selected according to the form of the first significant identifier in the module - the MODULE keyword. If it is written in lower case, then all the keywords of the module should also be lowercase, if MODULE, then uppercase.)

If a module is loaded dynamically, it can only be unloaded if it is not referenced from the import section of other modules, i.e. unloading should be done in reverse order. Unloading modules in OS A2 is done by the SystemTools.Free commands that accept the list of unloaded modules and SystemTools.FreeDownTo, which accepts the list of modules that should be unloaded after unloading all (recursively) references to them.

The operating environment tries not to unload the modules, allowing the programmer to choose the unloading time (including all depending on the required), since dynamic loading, apart from pluses, has significant drawbacks - with an inaccurate approach to unloading, you can get a situation where the module is not referenced from the import sections and it is unloaded by the user, and the callbacks set by him, for example, remain, which will cause an exceptional situation. Because, as the Little Prince said, “there is such a firm rule - I got up in the morning, washed, tidied myself up - and immediately put your planet in order”. In other words, if we instructed the callbacks, then we should remove them, which is done in the module finalizers. It is considered good practice to create default procedural implementations for procedural variables — stubs, which should be set during the initial initialization of the module containing such a variable and when finalizing the module that set this variable by assigning its implementation.

The situation with instances of reference types is more difficult because the link may be located in a module that did not understand the type in which the type is implemented, and formally there are no references in the import sections. In part, this can be dealt with by applying factories.

When a module is unloaded, the code and data are unloaded, with the exception of type descriptors, since instances of these types may remain in the system. All pointers in type descriptors, including method pointers in VMT, are assigned the value NIL. When referring to such entities, an exceptional situation will occur.

As you can see, the ascetic implementation of Oberon-Systems has both advantages and disadvantages, which should be taken into account in its developments. There are no real problems to eliminate these shortcomings, except for the complexity of the runtime environment and the compiler.

It is possible that with the help of the community these problems will be solved by moving Oberon into a new orbit.

Contents of the series


Source: https://habr.com/ru/post/258993/


All Articles