The MDL Programming Environment

Chapter 2. The Package System

The portion of the MDL environment which provides a uniform facility for lexical blocking is known as the Package System. In one sense it is the most basic part of the environment, since it enables many programmers to use each other's code without identifier conflicts.

In addition, the Package System is interfaced to a library facility (see section 4) by which MDL code may be stored and later loaded as needed.

The Package System is so basic to use of the MDL environment that (with a few exceptions) every subsystem or family of MDL functions described in this document is a 'package'.

2.1. The Theory of Lexical Blocking in MDL

Lexical blocking is implemented in MDL by means of OBLISTs and LISTs of OBLISTs. Changes of lexical context are performed using the SUBRs BLOCK and ENDBLOCK. The Package System provides a high-level interface to these low-level constructs.

The primary goal of a lexical blocking scheme is the prevention of identifier conflicts. Specifically, when your program references the variable X, it should be your X and not that of some other program. At the same time, it should not be necessary for a programmer to search every program previously written to verify that an identifier he wishes to use is not already 'taken'.

It should be clear that the simplest solution, a single OBLIST, will not satisfy either of these goals. With only one OBLIST there would necessarily be identifier conflicts, necessitating exhaustive searching for unique identifiers.

Obviously, programmers could put their program's identifiers on an OBLIST unique to that program. Unfortunately, such a solution addresses only half the problem. What happens when some other programmer wishes to use some of this code? He could insert the unique OBLIST for that program into the OBLIST path for his program; but the moment that is done he gets all the identifiers for that program, including local variables, internal data structures, and so on.

Consequently, we move to a situation where each program uses two OBLISTs: one for the identifiers that are local to the program, and one for the identifiers that are to be used by other programs. In the Package System, these are known as the 'internal' OBLIST and the 'entry' OBLIST.

Most of the identifiers in a program are local to it, and want to be placed on the internal OBLIST.

Therefore, in terms of an argument to the BLOCK SUBR, when a program is being loaded into MDL, the OBLIST path wants to be:

( internal-OBLIST
  entry-OBLIST
  <ROOT> )

With this OBLIST path, most ATOMs (identifiers) will be on the internal OBLIST (as READ puts unknown identifiers on <1 .OBLIST>), but the ATOMs for the entries and the ATOMs for the usual SUBRs will be available.

The only issue yet to be addressed is that of using an entry of a different program in your program. This is accomplished by adding the entry OBLISTs of any such programs to the path after ROOT:

( internal-OBLIST
  entry-OBLIST
  <ROOT>
  other-program-entry-OBLIST
  yet-another-program-entry-OBLIST )

As only the entry OBLIST, and not the internal OBLIST, of the program being used is added to the path, the chance of identifier conflict is lessened.

All that remains is to introduce the functions by which these various operations are performed.

2.2. Package System Overview

The functions which make up the Package System are:

  • PACKAGE. This indicates the start of a package of functions.

  • ENDPACKAGE. This indicates the end of the package of functions.

  • ENTRY. This indicates an ATOM which is to be made available outside the definition of this package of functions. All other ATOMs will not be directly available outside the package.

  • USE. This indicates a reference by name to another package of functions.

  • USE-DATUM. This indicates a reference by name to a data set.

  • DROP and L-UNUSE. These undo the effects of USE and USE-DATUM.

These functions are themselves part of a package named "PKG", which is preloaded into MDL.

2.2.1. Sample PACKAGE

A sample MDL PACKAGE is given with comments in order to demonstrate the usage of these functions.

<PACKAGE "HOUR-STRING">
"PACKAGE begins the package called HOUR-STRING."
<ENTRY TIME-STRING>
;"The atom TIME-STRING is an entry to this package;

it may be referenced by other packages by USEing HOUR-STRING." <USE "DATIME"> "Indicate that the package DATIME is used within the current package." <DEFINE TIME-STRING () <STRING <UNPARSE > " o'clock">> ;"Define this little function which returns a string telling the last hour in a strange format." <DEFINE HOURS () <1 >> ;"Define an internal function which is available only within the HOUR-STRING package, since its name is not in any ENTRY statement. Note that this function refers to RTIME, which is an ENTRY in the DATIME package." ;"The end of this little demonstration package."

2.3. PACKAGE

This function delimits the beginning of a package of functions. It takes one required argument, a STRING, which is the name of the package. This STRING uniquely identifies the package within a library of packages (see section 4).

In a PACKAGE those ATOMs which are specified as entries live in a separate OBLIST of their own, called the entry OBLIST. The ATOM naming this OBLIST is on the PACKAGE OBLIST and has the same name as the PACKAGE itself. Thus, an entry 'X' of a PACKAGE 'Y' would have as its 'full-trailer' name: X!-Y!-PACKAGE!- .

PACKAGE blocks (sets up) the current OBLIST path so that the ATOMs which are internal to the PACKAGE fall into an OBLIST which is not otherwise used. The ATOM naming this OBLIST is on the entry OBLIST of the PACKAGE, and is by default given a name created by putting the character 'I' at the beginning of the PACKAGE's name. An internal ATOM 'Z' in the PACKAGE 'Y' previously mentioned would have as its 'full-trailer' name: Z!-IY!-Y!-PACKAGE!- .

PACKAGE also keeps track of the fact that the particular PACKAGE named has been defined in this MDL process, by putting its name on the PACKAGE OBLIST.

<PACKAGE name:string
         iname:string
         size:fix
         isize:fix>

PACKAGE takes three optional arguments in addition to the required one (the optional arguments are ignored if name is already a PACKAGE):

iname is the name of the internal OBLIST of the PACKAGE; by default it is the name of the PACKAGE with the letter 'I' prefixed.

size is the number of buckets in the entry OBLIST; by default 19.

isize is the number of buckets in the internal OBLIST; by default 23.

In addition to PACKAGE, there exists the obsolete function RPACKAGE, documented here only because some programs still use it. The difference between them is that the entry OBLIST for an RPACKAGE is the ROOT OBLIST. The implication of inserting an entry into the ROOT is that this requires that the name of the entry be unique over all PACKAGEs, because the entry is, in effect, being promoted to the status of a SUBR. It is (in rare cases) useful to do this, but the correct way is with the function RENTRY (see section 2.3.1).

2.3.1. ENTRY

The ENTRY function applied to one or more ATOMs declares that these ATOMs are to be put into the OBLIST reserved for entries in this particular PACKAGE. Only ATOMs declared in this way will be accessible (in the normal course of events) to functions outside this PACKAGE.

It is possible to place some entries of a PACKAGE on the ROOT OBLIST using the function RENTRY. It is recommended that instead of using RPACKAGE in those rare cases where entries must go on the ROOT, RENTRY be used instead.

All ENTRY statements should appear immediately after the PACKAGE or RPACKAGE statement. Note: never put a USE statement before the ENTRY statements; if you do, you may get the ERROR message ALREADY-USED-ELSEWHERE, meaning that the name of an entry is conflicting with an ENTRY in one of the PACKAGEs you USEd. ENTRY will also give an ERROR if it is used outside the body of a PACKAGE.

2.3.2. USE

This function takes as arguments one or more STRINGs which are the names (as given to PACKAGE) of other PACKAGEs. EXTERNAL is a synonym of USE. USE causes the entry OBLISTs of the PACKAGEs named to be spliced into the current OBLIST path. Thus, references to entries of those PACKAGEs may be made after the USE, until the next ENDPACKAGE (or the next DROP or L-UNUSE if USE is being invoked outside a PACKAGE to load a file).

USE is consequently the mechanism for sharing code. If the PACKAGE being used is already loaded, its entries are made available; if not, the PACKAGE is loaded first (see section 4.1 for details on how this is accomplished).

2.3.3. USE-DATUM

USE-DATUM requires one STRING argument, the name of a data set. If the data set is not loaded, USE-DATUM loads it and creates an ATOM of the same name, on the USE-DATUM OBLIST, whose GVAL is the data set. USE-DATUM always EVALs to the data set named, regardless of whether it had to be loaded or not.

2.3.4. DROP and L-UNUSE

These functions take the same arguments as USE and USE-DATUM and undo their effects.

DROP simply splices the named PACKAGEs out of the current OBLIST path. A USE of a DROPped PACKAGE will not reload the PACKAGE but simply splice it back into the OBLIST path.

L-UNUSE splices the PACKAGE out and removes its name from the PACKAGE OBLIST, which will cause the entire PACKAGE to be reloaded if it is USEd again. L-UNUSE of a data set will remove its ATOM from the USE-DATUM OBLIST.

2.3.5. ENDPACKAGE

The ENDPACKAGE function of no arguments terminates the definition of the current PACKAGE and undoes the lexical blocking done by the PACKAGE function. The ENDPACKAGE statement should be the last one in the file.

2.3.6. PACKAGE Restrictions

There are some restrictions on what the user may do inside a PACKAGE. These are enforced by the Library System when the user attempts to submit a PACKAGE to a library.

A PACKAGE should not FLOAD or LOAD any file to obtain parts of itself. All such environment setup should be done with USE and USE-DATUM.

A PACKAGE may not reference any ATOM whose OBLIST path goes through the INITIAL OBLIST. All of a PACKAGE's non-entry ATOMs should fall naturally into the PACKAGE's internal OBLIST.

As mentioned before, the RENTRYs of a PACKAGE have the same OBLIST status as SUBRs, i.e., they must be unique among both all SUBRs and all PACKAGE entries.

2.3.7. ENTRY Name Conflicts

It is possible to have two or more PACKAGEs (not RPACKAGEs) which have entries (not RENTRYs) with the same PNAME. If the user needs both PACKAGEs at the same time, he may USE them both and refer to the ambiguous entries by their 'full trailer' names. All of the non-ambiguous entries in both PACKAGEs may still be referenced by PNAME only.