[Oberon] PO2013 - Modules.Load - recursion

Andreas Pirklbauer andreas_pirklbauer at yahoo.com
Sun May 31 07:49:33 CEST 2020

    > 1. Catch the cyclic import during compilation. This would
    > require modifying module ORB. It has been done before.
    > Not that hard. about 25-50 additional lines. 

I have been asked how to avoid cyclic imports. Here is one way
how to do it (see the references [1] - [4] at the end):

One needs to essentially store "module anchors" of *all* imported
(including re-imported) modules in the symbol files and propagate
such anchors across the entire import/export hierarchy.

There is just one small subtlety that absolutely needs to be addressed,
if you really want a solution that works in *all* possible cases: re-exports
and re-imports: In Oberon, a type T0 exported by a module M0 and
imported by a module M1 can be *re-exported* by M1 (for example as
a type of a non-exported record field) *and* their imports in another
client module M2 - which imports M1 - may be hidden. These so-called
"re-imports" absolutely need to be traced across the module hierarchy
and one way to do that is by propagating using module descriptions
called “module anchors” (essentially module name and key).

For some possible implementations of the full solution, see [1] - [4].

As one can see, the general problem of detecting cyclic imports
*at compile time* has actually been solved immediately after Oberon
was originally developed on Ceres around 1990, see [1]. I remember
vivid discussions on that at the CS institute in Zurich around this time.

A corollary is that *if* one implements the full solution, one no longer
needs to replace the simple recursive module loader by an iterative one,
as now the compiler *guarantees* that cyclic imports can never occur.

However, when the original Oberon system (and PO 2013) was built,
it was (probably) felt that this general solution is overkill. Here is why:
Since the *language* also does not *allow* cyclic imports - except when
you construct a pathological case like in the last post - why solving the
general problem?

A classic Wirthian simplification: No need to detect cyclic imports at
compile time because the language does not allow them. Therefore,
a simple and easy-to-understand recursive module loader suffices.
One may agree or disagree with this. After all, the extra cost of the
full solution is only 25-50 lines. But it’s the approach chosen in PO13.


[1] Griesemer R. On the Linearization of Graphs and Writing Symbol
Files. Computersysteme ETH Zürich, Technical Report No. 156a (1991)

[2] The OP2 compiler. Essentially implements [1]

[3] The oberonc compiler. Essentially implements [1]. (see 

[4] ORB13CheckCyclicImports.Mod, ORP13CheckCyclicImports.Mod at
This solution works on PO 2013, but as a proof-of-concept only. The
implementation cost is ~25 additional lines to module ORB. But it
essentially just retrofits the implementation of [1] by mimicking
module anchors in the symbol table itself (instead of using a
separate global module (called GMod or similar). So the module
anchors (=module descriptions, essentially name and key) come
as an *afterthought*. This solution is therefore not recommended.
If you want to check for all cyclic imports (including cyclic imports
among re-exported and re-imported modules) in a “clean” way, use
the "module table” model of [1], [2] or [3]

More information about the Oberon mailing list