2.2.3 Module activation

In all the examples seen so far the modules were accessed just once. In these cases, accessing the module via an access-clause causes the activation of the module.

Activating a module involves elaborating all the declarations and units that conform its prelude. Depending on the particular module definition that gets activated, this may involve all sort of side effects, such as allocating space for values and initializing them, opening files, etc. Once the modules specified in the access clause are successfully activated, the controlled clause gets elaborated itself, within the reach of all the publicized definitions by the activated modules as we saw in the last section. Finally, once the controlled clause has been elaborated, the module gets revoked by elaborating the serial clause in its postlude.

However, nothing prevents some given definition module to be accessed more than once in the same program. The following program, that makes use of the logger module, exemplifies this:

access Logger
begin originator := argv (1);
      log ("executing program");
      c ... c
      access Logger (originator := argv (1) + ":subtask";
                     log ("doing subtask")
                     c ... c)
end

In this program the module Logger is accessed twice. The code is obviously written assuming that the inner access clause triggers a new activation of the Logger module, allocating new storage and executing its prelude. This would result in the following log contents:

a.out: beginning of log
a.out: executing program
a.out:subtask: beginning of log
a.out:subtask: doing subtask
a.out:subtask: end of log
a.out: end of log

However, this is not what happens. The module gets only activated once, as the result of the outer access clause. The inner access clause just makes the publicized indicators visible in its controlled clause. The actual resulting log output is:

a.out: beginning of log
a.out: executing program
a.out:subtask: doing subtask
a.out:subtask: end of log

Which is not what we intended. Modules are not classes. If we wanted the logger to support several originators that can be nested, we would need to add support for it in the definition module. Something like:

module Logger =
   def int fd = stderr, max_originators = 10;
       int orig := 0;
       [max_originators]string originators;

       pub proc push_originator = (string str) void:
              (assert (orig < max_originators);
               orig +:= 1;
               originators[orig] := str);
       pub proc pop_originator = void:
              (assert (max_originators > 0);
               orig -:= 1);
       pub proc log = (string msg) void:
              fputs (fd, (originator[orig] /= "" | ": ") + msg + "'n");

       log ("beginning of log'n");
   postlude
       log ("end of log'n");
   fed

Note how in this version of Logger originators acts as a stack of originator strings, and it is not publicized. The management of the stack is done via a pair of publicized procedures push_originator and pop_originator. Our program will now look like:

access Logger
begin push_originator (argv (1));
      log ("executing program");
      c ... c
      access logger (push_originator ("subtask");
                     log ("doing subtask")
                     c ... c;
                     pop_originator)
end

And the log output is:

a.out: beginning of log
a.out: executing program
a.out:subtask: doing subtask
a.out: end of log