2.2.2 Accessing modules

Once a module is defined (see Writing modules) it can be accessed, provided it is within reach, using an access clause. The access clause identifies the modules to access and then makes the publicized definitions of these modules visible within a control clause.

For example, this is how we could use the logger definition module defined in a previous section to log the progress of some program that reads an input file and writes some output file:

access Logger
begin # Identify ourselves with the program name #
      originator := argv (1);

      # Read input file.  #
      if NOT parse_input (argv (2))
      then log ("error parsing input file"); stop fi;

      # Write output file.  #
      if NOT write_output (argv (3))
      then log ("error writing output file"); stop fi;

      log ("success")
end

In this case the controlled clause is the closed clause conforming the particular program, and the definitions publicized by the logger module, in this case originator and log, can be used within it.

2.2.2.1 Accessing several modules

An access clause is not restricted to just provide access to a single module: any number of module indicators can be specified in an access clause. Suppose that our example processing program has to read and write the data in JSON format, and that a suitable JSON library is available in the form of a reachable module. We could then make both logger and json modules accessible like this:

access Logger, JSON
begin # Identify ourselves with the program name #
      originator := argv (1);

      JSONVal data;

      # Read input file.  #
      if data := json_from_file (argv (2));
         data = json no val
      then log ("error parsing input file"); stop fi;

      # Write output file.  #
      if not json_to_file (argv (3), data)
      then log ("error writing output file"); stop fi;

      log ("success")
end

In this version of the program the access clause includes the module indicator json, and that makes the mode indicator jsonval and the tags json no val, json from file and json to file visible within the program’s closed clause.

Note that the following two access clauses are not equivalent:

access Logger, JSON C ... C;
access Logger access JSON C ... C;

In the first case, a compilation time error is emitted if there is a conflict among the publicized definitions of both modules; for example, if both modules were to publicize a procedure called log. In the second case, the declaration of log publicized by Logger would hide the declaration of log publicized by JSON. This also has implications related to activation, that we will be discussing in a later section.

2.2.2.2 The controlled clause

The controlled clause in an access clause doesn’t have to be a serial clause, like in the examples above. It can be any enclosed clause, like for example a loop clause:

proc frobnicate frobs = ([]Frob frobs) void:
   access Logger to UPB frobs
                 do log ("frobnicating " + name of frob);
                    frobnicate (frob)
                 od

2.2.2.3 The value yielded by an access clause

The elaboration of an access clause yields a value, which is the value yielded by the elaboration of the controlled clause. Since the later is an enclosed clause, coercions get passed into them whenever required, the usual fashion.

We can see an example of this in the following procedure, whose body is a controlled closed clause that yields a real value:

proc incr factor = (ref[]real factors, int idx) real:
   access logger (log ("factor increased"); factors[idx] +:= 1.0)

Note how the access clause above is in a strong context requiring a value of mode real. The value yielded by the access clause is the value yielded by the controlled enclosed clause, which in this case is a closed clause. The strong context and required mode gets passed to the last unit of the closed clause (the assignation) which in this case yields a value of mode ref real. The unit is coerced to real by dereferencing, and the resulting value becomes the value yielded by the access clause.

2.2.2.4 Modules accessing other modules

A definition module may itself access other modules. This is done by placing the module text as a controlled clause of an access clause. Suppose we rewrite our logger module so it uses JSON internally to log JSON objects rather than raw strings. We could do it this way:

module logger =
   access json
   def int fd = stderr;
       pub string originator;
       pub proc log = (string msg) void:
              fputs (fd, json array (json string (originator),
                                     json string (msg)));

       log (json string ("beginning of log\n"));
   postlude
       log (json string ("end of log\n"));
   fed

Note how this version of logger uses a few definitions publicized by the json module.

A program accessing logger will not see the definitions publicized by the json module. If that is what we intended, for example to allow the users of the logger to tweak their own JSON, we would need to specify it this way:

module logger =
   access pub json
   def c ...as before... c fed

In this version the definitions publicized by json become visible to programs accessing logger.