Chapter 16 - With All My Worldly Goods...

Prefixed Blocks and Separate Compilation

Avoiding re-inventing the wheel

One of the most labour saving aspects of high level languages, like SIMULA, is the provision of features which allow a piece of the program with a well defined function to be used wherever needed without rewriting it each time. Loops, procedures, recursive calls, classes and coroutines all help with this. Even these features must be written again in each program where they are used and not simply named.

Certain commonly used features are available in all programs, without any declarations being necessary. These are the system features. This is very useful, but not all the procedures or classes required for a particular programmer to produce his programs will be found in the list of SIMULA system features.

Some versions of SIMULA have extra system features, but programs written for such systems will not run on standard systems. Even the best of these extended systems cannot have all the features which may be required.

In practice each type of program will probably want to use a set of features suited to the problems being solved. We have seen that word processing programs can make use of many features which might appear in several different parts of a system or in completely different programs. The word processing programmer would like to have these available as system features. On the other hand, a mathematical programmer would probably find little to interest him in line breakers, chapter classes or whatever. He would prefer to have complex number procedures, matrix classes or things of that sort.

There are two possible solutions to the problem. One is to keep extending the language to try to provide all the possible features which will be required. This leads to impossibly complicated languages and cumbersome compilers and runtime systems. I will not name any of the languages which have fallen into this trap. If you ever meet any, you will recognise the description.

SIMULA is already a fairly substantial language. Its origins in Algol60 have made it larger than it needs to be. To add further complication is not a good idea. In general, the system features in SIMULA only provide facilities which would be impossible or very inefficient if they were written in SIMULA itself. This leaves the second solution.

If a programmer could produce a library of procedures and classes suited to his particular needs and make it available wherever he needed it, in a simple way, the problem would be solved. This concept is sometimes called a library and sometimes a package.

Example 16.1: A simple prefixed block.

    begin

       class MyFeatures:
       begin

          procedure OutLine(T); text T;
          begin
             OutText(T);
             OutImage
          end..of..OutLine;

          procedure DoubleSpace(T); text T;
          begin
             OutLine(T);
             OutImage
          end..of..DoubleSpace;

       end--of--MyFeatures;

       MyFeatures
       begin
          OutLine("First");
          DoubleSpace("Second");
          OutLine"Fourth")
       end--of--prefixed--block

    end**of**program
It is also useful sometimes to be able to use just one or two features, independently of a package. If these can be included in a concise way, it can save effort. This can be done as well, using "separate compilation".

Finally, it is useful to be able to compile a package separately, as well as odd procedures or whatever. The advantage is that this part of your program is already compiled. It does not need to be completely reprocessed by the compiler. This can make compilation of the program using the separately compiled feature much faster. Since it is often necessary to recompile a program several times before it is correct and working, the saving can be considerable.

Making packages with prefixed blocks

When we want to make the features of a class available in another class, we prefix the new class with the old one. In a similar way, we can prefix a block with a class. This makes all the features of the class available inside that block. Example 16.1 shows a prefixed block, using the features available in class MyFeatures.

Clearly this example gains no advantage from using a prefixed block. We could simply have declared the two procedures outside of MyFeatures and called them anywhere in the program. It merely serves to illustrate the nature of a prefixed block.

In its simplest form, a prefixed block is a prefixing class identifier, followed by the keyword begin, followed by a sequence of declarations and statements, followed by the keyword end.

All the visible attributes of the prefixing class are available inside the block that it prefixes without further declaration.

The declaration of the prefixing class must be made in the block which immediately encloses the block that it prefixes. In the example, the class MyAttributes is declared in the program block. This is also the block immediately surrounding the prefixed block and so the program is legal SIMULA.

Note that the use of the keyword this inside the prefixing class is both meaningless and forbidden.

In general, this simple kind of prefixed block is only of academic interest. One simple extension is to prefix more than one block with the class. This is possible only where the blocks meet the condition in the last paragraph but one, i.e. they are both sub-blocks of the block where the prefixing class is declared. Different blocks can also be prefixed with different classes, but again the usefulness is limited if the full class declaration has to be made in the immediately enclosing block.

A rather more interesting possibility is to use parameters to the prefixing class. This allows different blocks with the same prefix to be set up differently. A somewhat contrived example is shown in example 16.2. Note that the parameter values must follow the class identifier which prefixes each block.

Example 16.2: Prefixed blocks with parameters.

    begin

       class Printing(Spaces); integer Spaces;
       begin

          procedure OutLine(T); text T;
          begin
             integer Count;
             OutText(T);
             for Count := 1 step 1 until Spaces do OutImage
          end..of..OutLine;

       end--of--Printing;

       Printing(1)
       begin
          OutLine("First");
          OutLine("Second")
       end--of--first--prefixed--block;

       Printing(2)
       begin
          OutLine("First");
          OutLine("Third")
       end--of--second--prefixed--block

    end**of**program
Still, there is no great benefit to be seen from such a device. In itself the prefixed block provides no great power. It is, however, immensely powerful in practice, when combined with other features.

The first benefit is that two system classes are provided for use as prefixes. Each covers a particular area which is not of sufficiently general interest to be included in the basic language. These are SIMSET, which provides powerful list handling, and SIMULATION, which provides discrete event simulation features. They are described in chapter 17 and chapter 19 respectively.

The second benefit is that a class can be compiled on its own then brought into the programs you write by a single line "external" declaration. It is this second point that we shall consider next.

Using separately compiled prefixes

A class like MyFeatures is suitable for separate compilation. It contains no uses of non-system identifiers, other than those declared inside itself. It is self-contained.

The precise way of specifying that a class is to be compiled on its own rather than as part of a program, varies amongst SIMULA systems. You should check the documentation for the one you wish to use. Some older systems may not even allow it, in which case you are advised to move to one that does, if at all possible.

When a class is separately compiled one or more extra files are generated, compared with a normal compilation. These are called attribute files and contain details of the visible attributes of the class. They will be needed by the compiler whenever you compile a program which contains an external declaration of the class. Again, you should consult carefully the documentation for the system you are using.

To use a separately compiled class, you must include an external declaration in the place of the full class declaration. Example 16.3a shows example 16.1 with MyFeatures assumed to have been compiled separately.

The syntax of this external declaration is quite simple. The keyword external is followed by the keyword class, followed by the class identifier. This identifier will enable the compiler to locate the attribute file or files for MyFeatures. These contain all the information it needs about the class. The parameters, if any, should not be specified. They will be described in the attribute file or files.

Note that the external declaration is given at exactly the place where the full class declaration was given, so as not to violate the rule concerning declaration inside the block enclosing the prefixed block.

Examples 16.3: Using a separately compiled class as a block prefix.

a)   begin

        external class MyFeatures;

        MyFeatures 
        begin
           OutLine("First");
           DoubleSpace("Second");
           OutLine(Fourth")
        end- -of-- prefixed-- block

     end**of**program

b)   begin

        external class MyFeatures = "ERCS12.MYFEATURESATR";

        MyFeatures
        begin
           OutLine("First");
           DoubleSpace("Second");
           OutLine("Fourth")
        end--of--prefixed--block

     end**of**program
Example 16.3b shows the same program with a slight twist. It may be that the particular SIMULA system has file names which do not conform to the syntax of SIMULA identifiers. It may be that you need to show that the attribute files for the class are in another directory or belong to a different user. It may even be that you wish to give the class a different name in your program to the one it had when it was separately compiled. The use of an "external identifier" as well as an "internal identifier" can allow for all of these.

The external identifier is an optional addition in an external declaration. It follows the internal identifier, separated by an equals sign and enclosed in double quotes. The use made of this string is system dependent, although I have tried to give examples of typical uses.

The example in 16.3b shows how it would be possible to specify on the 2900 EMAS SIMULA system that the attribute files for the class known with the program as MyFeatures actually belong to user ERCS12.

Yet again, read the documentation carefully to see how your system interprets the external identifier.

Note how much more compact even such a trivial program can become.

Exercises

16.1 Try out the separate compilation features of your SIMULA system, using the example in 16.3.

16.2 Rewrite example 16.2 using separate compilation.

16.3 Start building your own libraries. Remember to include only items needed for a particular type of programming in each one.

Other possibilities with prefixed blocks

It is possible to have classes as well as procedures inside the prefixing class. This would allow us to have the list processing facilities which we developed in chapter 10 available in a package.

It is also possible to use one separately compiled class to prefix another. This would allow one package to be an extension of another. This is important, because a block can only be prefixed by one class. To make the attributes of two separately compiled classes available, it is necessary to make one a subclass of the other and use the subclass as a prefix.

A common way of organising this is to have your most widely used features declared in a basic class. This is then used to prefix a wide range of more specialised classes. These can, in turn, be used to prefix even more specialised ones and so on.

Examples 16.4a, b and c show how this works.

This ability to extend the language according to the special needs of a user led the original designers of SIMULA to call it the "Common Base Language". Their intention was that very few people would need to write in straight SIMULA. Most would use packages of ready built components suited to their needs.

Examples 16.4: Subclass prefixing of blocks.

a) The parent class, compiled separately.

        class MyFeatures;
        begin
           procedure OutLine(T); text T;.......;
           procedure DoubleSpace(T); text T;........;
           etc.
        end--of--MyFeatures;

b) A more specialised sub-class, also compiled separately.

        external class MyFeatures;
        MyFeatures class LinkList;
        begin
           class Linker;.......;
           class ListHead;.......;
           etc.
        end--of--LinkList;

c) A program using LinkList.

        begin

           external class LinkList;
        
           LinkList
           begin
              ref(Linker) L;
              Linker class LinkObj;.......;
              etc.

              OutLine("Hello");
              etc.
           end--of--prefixed--block;

        end**of**program

Instructions in prefixing classes

The class which prefixes a block can contain instructions and an inner statement, in the same way as any other class. These are executed in the same way as for a class which prefixes another class.

The instructions up to the inner statement, if present, or the final end, if not, are executed when the block which is prefixed is entered. If there is an inner statement, the instructions which follow it are executed when the prefixed block is left through its final end.

Example 16.5 shows a trivial example of this.

The initial sequence can be used to set values in variables, print headings and any other preliminary tasks. The prefixed block is then executed. The sequence after an inner can then be used to tidy up, print summaries, close files or any concluding tasks.

Programs as prefixed blocks

In fact the program block is really inside a prefixed block, whose prefix does not have to be written. This brings into the program two libraries of system features, one of which prefixes the other.

The outermost class is called Environment. This contains the system procedures not connected with files, such as Call.

This class prefixes one called BasicIO, which contains the declarations of File and all its subclasses. SysIn and SysOut are declared here.

These classes can be regarded as having been separately compiled to produce the runtime system. We have already seen most of the contents of BasicI0. The environment is covered more fully in chapter 20.

A normal program can be regarded as being as shown in figure 16.1. Note that BasicIO is shown as having two parameters. These are the image lengths to be used for SysIn and SysOut respectively. They are set by the system to match the devices being used for default input and output.

Figure 16.1: The program as a prefixed block.

When you write

        begin
           ...
           ...
        end++of++program
it is interpreted by the system as
        BasicIO(InLength,OutLength)
        begin
           inspect SysIn do
              inspect SysOut do
              begin
                 ...
                 ...
              end++of++program
        end--of--prefixed--block--and--inspections
Examples 16.5: Prefixed block with actions.
a) The separately compiled class.

        class TopandTail;
        begin
           OutText("Top and Tail, version 1.1");
           OutImage;
           inner;
           OutText("Top and Tail has finished");
           OutImage
        end--of--TopandTail;

b) A program using TopandTail.

        begin

           external class TopandTail;

           TopandTail
           begin
              OutText("The prefix block's actions come here");
              OutImage
           end--of--prefixed--block

        end**of**program

Using externally declared items directly

It is also legitimate to compile a class or procedure separately for use as a simple class or procedure. It is not just prefixes of blocks which can be treated in this way.

Such classes and procedures are introduced by external declarations, as we have seen with prefixing classes. For a procedure, the keyword procedure is used where previously the keyword class was. Having been declared in this way, these procedures may then be used as if they had been fully declared inside the program.

The types of block in a SIMULA program

We have now covered all the types of block which can occur in a SIMULA program, and so a brief summary is perhaps in order.
Program block
is the outermost block of a full program. Everything from its begin to its end is part of the main program. It acts as if it were enclosed by an invisible block prefixed by BasicIO and as if it was within nested inspection blocks for both SysIn (the outer inspection) and SysOut (the inner). BasicIO is a subclass of class Environment (see chapter 20).

Sub-block
is any sequence of instructions enclosed within a begin/end pair, containing at least one declaration and not falling into any other category of block. Note that it may contain only declarations. If it contains only statements and no declarations, it is a compound statement and not a block.

Procedure body
is the statement following the header of a procedure, which specifies the actions to be performed by that procedure. It is regarded as a block even if it is a simple statement or a compound statement, containing no declarations.

Class body
is the equivalent of a procedure body for a class.

Prefixed block
is any sequence of instructions enclosed within a begin/end pair and prefixed by a class identifier. It is regarded as a block even if it contains no declarations, unlike a sub-block.

Inspection block
occurs inside inspection statements. Those statements within the when clauses, otherwise clauses or, within simple inspection statements, following the do are all inspection blocks. They are regarded as blocks even when they are simple statements or compound statements containing no declarations.
The significance of declarations within blocks, especially on access to those items, is considered further in chapter 18, when the concept of "scope" is considered in some detail.

External declaration of non-SIMULA procedures

As well as useful libraries of SIMULA classes and procedures, it is often important to be able to use libraries of procedures written in other programming languages. Mathematical and graphical libraries are common examples, which are often written in Fortran or the assembly language of a particular computer. The syntax of SIMULA allows for such procedures being used in SIMULA programs, but, unfortunately, different SIMULA systems have interpreted the meaning of such declarations rather differently.

It is not sensible to cover all the variations here. The only reliable guide is the documentation for the system you are using.

Be especially careful that such differences do not cause problems when moving your programs from one system to another. Check also the range of parameter types allowed, which can be very different between systems where "foreign" language procedures are used.

One useful device is to create a separately compiled class containing SIMULA procedures which call all the non-SIMULA procedures in a particular library. This class is then the only place where the non-SIMULA procedures need to be declared. Any programs using them can be prefixed with the class containing their declarations and use the SIMULA procedures which call them. This means that when you move to another system, only the interfacing class needs to be changed to match the form of non-SIMULA external declaration for that system.

Summary

We have seen how to make a package of features available, using a prefixed block.

We have seen how separately compiled SIMULA modules, especially packages contained in classes, can be used through external declarations.

The notion of the program block as being enclosed in an invisible prefixed block and two inspections has been introduced. This explains the presence and meaning of system features in SIMULA.

A summary of the types of block possible in SIMULA has been given.

We have noted that is is possible to use separately compiled procedures from other programming languages, by making external declarations. We have also noted that the use of such declarations varies widely amongst existing SIMULA systems and the problems that this can cause, when moving programs from one to another.