Cours Modula-2 and Oberon Niklaus Wirth, tutoriel & guide de travaux pratiques en pdf.
Procedure Types
An uncontroversial, fairly straight-forward, and most essential innovation was the procedure type, also adopted from Mesa. In a restricted form it had been present also in Pascal, even ALGOL, namely in the form of parametric procedures. Hence, the concept needed only to be generalized, i.e. made applicable to parameters and variables. In respect to Pascal (and ALGOL), the mistake of incomplete parameter specification was amended, making the use of procedure types type-safe. This is an apparently minor, but in reality most essential point, because a type-consistency checking system is worthless if it contains loopholes.
The Type CARDINAL
The 1970s were the time of the 16-bit minicomputers. Their word length offered an address range from 0 to 216-1, and thus a memory size of 64K. Whereas around 1970, this was still considered adequate, it later became a severe limitation, as memories became larger and cheaper. Unfortunately, computer architects insisted on byte addressing, thus covering only 32K words.
In any case, address arithmetic required unsigned arithmetic. Consequently, signed as well as unsigned arithmetic was offered, which effectively differed only in the interpretation of the sign bit in comparisons and in multiplication and division. We therefore introduced the type CARDINAL (0 … 216-1) to the set of basic data types, in addition to INTEGER (-215 … 215-1). This necessity caused several surprises. As a subtle example, the following statement, using a CARDINAL variable x, became unacceptable.
x := N-1; while x ≥ 0 do S(x); x := x-1 end
The correct solution, avoiding a negative value of x, is of course x := N; while x > 0 do x := x-1; S(x) end
Also, the definitions of division and remainder raised problems. Whereas mathematically correct definitions of quotient q and remainder r of integer division x by y satisfy the equation
q×y + r = x, 0 ≤ r < y
which yields, for example (7 div 3) = 2 and ( -7 div 3) = -3, most available computers provided a division, where (-x) div y = -(x div y), that is, (-7 div 3) = -2.
Low-level Facilities
Facilities that make it possible to express situations which do not properly fit into the set of abstractions constituting the language, but rather mirror properties of the computer, are called low-level facilities. Although necessary at the time – for example to program device drivers and storage managers – I believe that they were introduced too light- heartedly, in the naive assumption that programmers would use them only sparingly and as a last resort. In particular, the concept of type transfer function was a major mistake. It allows the type identifier T to be used in expressions as a function identifier: The value of T(x) is equal to x, whereby x is interpreted as being of type T, i.e. x is cast into a T. This interpretation inherently depends on the underlying (binary) representation of data types. Therefore, every program making use of this facility is inherently implementation-dependent, a clear contradiction of the fundamental goal of high-level languages.
In the same category of easily misused features is the variant record, a feature inherited from Pascal (see [13], Chap. 20). The real stumbling block is the variant without tag field. The tag field’s value is supposed to indicate the structure currently assumed by the record. If a tag is missing, no possibility exists to determine the current variant. It is exactly this lack which can be misused to access record fields with intentionally « wrong » types.
What was left out
C.A.R. Hoare used to remark that a language is indeed defined by the features it includes, but more so even by those which it excludes. My own guide-line was to omit features, whose correct semantics and best form were still unknown. Therefore it is worth while mentioning what was left out.
Concurrency was a hot topic, and still is. There was no clear favorite way to express and control concurrency, and hence no set of language constructs that clearly offered themselves for inclusion. One basic concept was seen in concurrent processes synchronized by signals (or conditions), and involving critical regions of mutual exclusion in the form of monitors [6]. Yet, it was decided that only the very basic notion of coroutines would be included in Modula-2, and that higher abstractions should be programmed as modules based on co-routines. This decision was even more plausible, because the primitives could well be classified as low-level facilities, and their realization encapsulated in a module (see [13], Chap. 30 and 31).
We also abandoned the belief that interrupt handling should be treated by the same mechanism as programmed process switching. Interrupts are typically subject to specific real- time conditions. Real-time response is impaired beyond acceptable limits, if interrupts are handled by very general, complicated switching and scheduling routines.
Exception Handling was widely considered as a must for any language suitable for system programming. The concept originated from the need to react in special ways to rare situations, such as arithmetic overflow, index values being beyond a declared range, access via nil-pointer, etc., generally conditions of “unforeseen” error. Then the concept was extended to let any condition be declarable as an exception requiring special treatment. What lent this trend some weight was the fact that the program handling the exception might lie in a procedure different from the one in which the exception occurred (was raised), or even in a different module. This precluded the programming of exception handling by conventional conditional statements. Nevertheless, we decided not to include exception handling (with the exception of the ultimate exception called Halt).
Modula-2 features pointers and thereby implies dynamic storage allocation. Allocation of a variable x↑ is expressed by the standard procedure Allocate(x), typically taking storage from a pool area (heap). A return of storage to the pool is signaled by the standard procedure Deallocate(x). This was known to be a highly unsatisfactory solution, because it allows to return records (storage) that are still reachable from other, valid pointer variables, and therefore constitutes a rich source of disastrous errors.
The alternative is to postulate a global storage management, which retrieves unused storage automatically, that is, a garbage collector. We rejected this for several reasons.
1. I believed it was better if programmers would devise their own storage managers, thus obtaining the most effective use of storage for the case at hand. This was of great importance at the time, considering the small memory size, such as 64k bytes for the PDP-11, on which Modula-2 was first implemented.
2. Garbage collectors could activate themselves at unpredictable times, and hence preclude dependable real-time performance, which was considered an important domain of applications of Modula-2.
3. Garbage collectors must rely on incorruptible meta-data about all variables in use. Given the many loopholes for breaching the typing system, it was considered impossible to devise secure garbage collection with a reasonable effort. The flexibility of the language had become its own impediment. Even today, providing a garbage collector with an unsafe language is a sure guarantee for occasional crashes.
Implementations
Although a preliminary technical memorandum stating certain goals and concepts of the new language was written in 1977, the effective language design took place in 1978-79. Concurrently, a compiler implementation project was launched. The available machinery was a single DEC PDP-11 with a 64K-byte store. The single-pass strategy of our Pascal compilers could not be adopted; a multipass approach was unavoidable in view of the small memory. It had actually been the Mesa implementation at the Palo Alto Research Center (PARC) of Xerox which had proved possible what I had believed to be impracticable, namely to build a complete compiler operating on a small computer. The first Modula-2 compiler, written by K. van Le in 1977 consisted of 7 passes, each generating sequential output written onto the 2M-byte disk. This number was reduced in a second design by U. Ammann to 5 passes in 1979. The first pass, the scanner, generated a token string and a hash table of identifiers. The second pass (parser) performed syntax analysis, and the third pass handled the task of type checking. Passes 4 and 5 were devoted to code generation. This compiler was operational in early 1979.
In the meantime, a new Modula-2 compiler was designed in 1979-80 by L. Geissmann and Ch. Jacobi with the PDP-11 compiler as a guide, but taking advantage of the features of the new Lilith computer. Lilith was designed by the author and R. Ohran along the guide lines of the Xerox Alto [12, 19, 21]. It was based on the excellent Am2901 bit-slice processor of AMD, and it was microprogrammed. The new Modula-2 compiler consisted of only four passes, code generation being simplified due to the new architecture. Development took place on the PDP-11. Concurrently, the operating system Medos was implemented by S. Knudsen – a system essentially following in the footsteps of batch systems with program load and execute commands entered from the keyboard. At the same time, display and window software was designed by Ch. Jacobi. It served as the basis of the first application programs, such as a text editor – mostly used for program editing – featuring the well-known techniques of multiple windows, a cursor/mouse interface, and pop-up menus.
By 1981, Modula-2 was in daily use and quickly proved its worth as a powerful, efficiently implemented language. In December 1980, a pilot series of 20 Liliths, manufactured in Utah under the supervision of R. Ohran, had been delivered to ETH Zürich. Further software development proceeded with a more than 20-fold hardware power at our disposal. A genuine personal workstation environment had successfully been established.
During 1984, the author designed and implemented yet another compiler for Modula-2. It was felt that compilation could be handled more simply and more efficiently, if full use were made of the now available larger store which, by today’s measures, was still very small. Lilith’s 64K-word memory and its high code density allowed the realization of a single-pass compiler. This resulted in a dramatic reduction in disk operations. Indeed, compilation time of the compiler itself was reduced from some 4 minutes to a scant 45 seconds.
The new, much more compact compiler retained the partitioning of tasks. Instead of each task constituting a pass – with sequential input from, and output to disk – it constituted a module with a procedural interface. Common data structures, such as the symbol table, were defined in a data definition module imported by (almost) all other modules. These modules represented a scanner, a parser, a code generator, and a handler of symbol files. During all these re-implementations, the language remained practically unchanged. The only significant change was the deletion of export lists in the definition parts of modules. The compilation of imports and exports constituted a remarkable challenge under the objective of economy of linking data and under the absence of automatic storage management [24].
Over the years, it became clear that designers of control and data acquisition systems found Modula-2 particularly attractive. This was due to the existence of both low-level facilities to control interfaces, and of modules to encapsulate critical, device-specific parts. A Modula -2 compiler was offered by two British companies, but Modula-2 never experienced the same success as Pascal, and it never became as widely known. The primary reason was probably that Pascal had been riding on the back of the micro-computer wave invading homes and schools, reaching a class of people not infected by earlier programming languages and habits. Modula-2, on the other hand, was perceived as merely an upgrade on Pascal, hardly worth the effort of a language transition. The author, however, naively believed that everyone familiar with Pascal would happily welcome the additions and improvements of Modula-2.