Pull to refresh

Modula-3. The article from “Computer newspaper” N12 2000

Reading time9 min
Views691
Original author: Evgeny Shcherbatyuk

  One of the main tenets of the Unix philosophy is that a good tool for a good cause. Suppose you have a task to develop a large application that should have multiple threads of execution, possibly be distributed and, of course, have a graphical interface. I would like to make such a program quickly and without unnecessary mistakes. 

I think the first question to ask in a situation like this is, "Which programming language is right?" C is not a bad choice, but not for such a project. It does not scale very well, and does not have the means of working with processes at all. Then C++? But C++ is a complex language, and past experience has shown that it will take a fair amount of time to debug memory allocation problems. What else? 

There is a well-designed tool for just such a job. It is a Modula-3 language developed and implemented by the Digital Equipment Corporation Systems Research Center (SRC). Modula-3 is a modern, modular, object-oriented language. Other features include automatic memory management (built-in garbage collector), exception handling, support for dynamic types, and multi-threaded programming. 

The SRC implementation includes a compiler, a minimal recompilation system (m3build), and a wide range of libraries and sample applications. It must be said that SRC Modula-3 is a free system supplied with source code, including a compiler and a run-time kernel. In addition, SRC Modula-3 has been implemented for a dozen platforms, including Windows 95/NT.

  The goal of the developers of the language, in their own words, was not innovation, but the careful selection and consolidation of ideas, time-tested and proven to be useful in practice. Modula-3 is a simple but full-featured language for building large and reliable software packages with a long life cycle.

  To imagine Modula-3, you can take Pascal as a starting point and rework it for real system programming. Strictly speaking, the immediate ancestors of Modula-3 are Modula-2+, Cedar and Euclid, more distant - Simula. New, in comparison with Pascal, language capabilities, by and large, serve either for structuring large-scale software systems, or provide opportunities for low-level (machine-dependent) programming. Real applications need both. 

For structuring large systems, the "interface" - "implementation" construction is used, where the first is separated from the second. If the system is structured in terms of interfaces, then the implementations of its corresponding parts are independent and can be changed as needed without the slightest risk of compromising the integrity of the system or introducing "induced", that is, secondary, errors. 

In addition, Modula-3 has single inheritance objects. There is still controversy surrounding multiple inheritance, but the fact is that this concept can significantly complicate both the language and the applications created with it, both conceptually and in implementation. In Modula-3, an object looks simple - a record with fields (object state) and a set of methods (object behavior).

  Garbage collection is another of the most important features of the language. In recent years, it has ceased to be considered an unnecessary luxury, realizing that it is the automatic management of memory allocation that guarantees the reliability and survivability of programs. Without this, the programmer has to constantly decide who will be the "master" of the memory. 

For example, if I pass you a pointer to a structure, are you allowed to save that address for future reference? If so, who will be in charge of freeing memory in a vague future: you or me? The question of when memory can be safely freed is usually resolved by maintaining a reference count. Well, now tell yourself, how often do programmers conclude and / or execute such agreements in real life? And in general, how many people are aware of the existence of the problem? 

The result that we see all over the place is programs with massive leaks, dangling pointers and cross-use of memory for different purposes. In addition, even when everything is in order, in C, exiting the body of the procedure by jumping over the label can lead to memory loss. The same issue is observed in C++ exception handling. That is, even in normal situations, when an early exit from the procedure occurs, manual memory release becomes problematic. The garbage collector solves all of these problems in one fell swoop. And the best part is that the collector in the SRC implementation has excellent performance. This is the result of several years of industrial operation and algorithm tuning. 

Most modern programs and systems have at least a touch of multitasking and asynchrony. All graphical applications are by definition asynchronous: they are entered and controlled by the user. Multithreaded and multiprocessor applications are also essentially asynchronous. Therefore, it should come as a surprise that very few programming languages ​​have built-in concurrency controls. 

Multitasking is at the mercy of the programmer. Most often, programmers use timers and signal handlers. This approach works for very simple applications and quickly fails when the complexity of the programs increases or when the programmer tries to use two different libraries at the same time, each of which implements multitasking in a different way. Does the title of the nested event loop problem tell you anything? There is even a phrase: "The event loop is multitasking for the poor." 

Modula-3 has standard tools for creating parallel threads of execution, including support for lock management.

  All standard libraries in the SRC implementation are deadlock safe. The X-Windows GUI library is not only safe, but it itself uses threads to perform long-running operations in the background, such as following a click on a button.

  The key to code reuse is the so-called generic Modula-3 interfaces. One of their main uses is to define container types such as stacks, lists, and queues. Common interfaces allow you to make the code that describes the container object independent of the content type. That is, it is enough to define the "Table" interface to instaniate it for integers or real numbers, or for other data types. Type parameterization and the template mechanism in C++ have a similar purpose, but the Modula-3 way is simpler and clearer. 

Finally, a few words about low-level machine-dependent programming. One of the important lessons that the C programming language has taught the world is that real systems require a significant amount of machine programming. So, there is a possibility of low-level programming in Modula-3. 

Any module marked "unsafe" has full access to machine-dependent operations. These include address arithmetic, manual allocation and deallocation of memory, arbitrary type conversion, machine-dependent arithmetic, and more. These possibilities are fully demonstrated by the implementation of the I/O system. Its lower level makes full use of machine-dependent operations to ensure performance. 

However, this is not all. Strictly speaking, it is practically very difficult to draw the line between the standard facilities of the Modula-3 language and its application libraries. Therefore, you can rewrite both I / O and multitasking management, and more. This feature is the brightest characteristic of the power of Modula-3 as a system programming language. In addition to the above, mention should be made of the possibility of using libraries written in C by importing them as "dangerous" interfaces. 

It looks like we quietly moved on to discussing the SRC Modula-3 programming system. Let's restrict ourselves to the Windows implementation. To use Modula-3, you must install a certain subset of MS VC++ not lower than the fourth version. The fact is that Modula-3 has a compiler, a sophisticated make program (m3build), but uses the standard VC link editor. 

There is no shell, or programming environment, as such. However, there is no particular need for it. Program texts are created in text format by any suitable editor, a small control file is written, which lists the program modules and the necessary resource files. After that, m3build is launched, which does everything that needs to be done, and never recompiles those modules where no changes were made. By the way, there is no native debugger in the SRC Modula-3 implementation for Windows either, but there is a compiler option to enable debug information, so you can use the standard VC debugger. 

A good modular object-oriented language is a good start, but perhaps not enough to seriously talk about changing the programming language. First, I would like to have confidence in the language itself. There is not much to say here: either you trust Digital Equipment and Olivetti, who jointly developed Modula-3 and have kept it in working order for over 5 years, or not. 

Second, the real value of the language comes from having good, highly reusable standard libraries. The answer is that this is the strong point of the SRC Modula-3 programming system. Most of its libraries are classified as "industrial" and are the product of actual exploitation and development. They are also better documented than many commercial libraries. 

Libm3 is the workhorse of Modula-3. It is roughly analogous to libc, the standard C library, but much richer. For example, Libm3 defines a set of abstract types for I / O called "readers" and "writers". They provide an abstract interface for streaming buffered I / O. The classic devices stdin, stdout, and stderr fall into the described category. In addition to console streams, you can also open file streams, and the 'Pipe' type has been added to the 'File' and 'Terminal' types. 

For writing to streams, the Fmt interface is used, which can be compared to printf in C. One of the problems with C is that the programmer passes one data type to printf and tries to output it in a different format. Fmt's interface is specifically designed to keep printf flexible, but not inherit problems. 

Further, Libm3 has a number of common interfaces that define basic container types. These include tables, lists, and sequences. The table is an appropriately indexed array. A list resembles Lisp-style lists, and a sequence is a one-dimensional variable-length array. 

Finally, Libm3 provides a simple save mechanism called 'Pickle'. Recall the tedious task of transforming complex data structures when writing their contents to disk or reading from disk. It is so difficult and error-prone that many programmers avoid it with all their might. Pickle eliminates the need to write such code altogether. Since the executing system already "knows" the layout of the data structure in memory, it is also engaged in writing data to a stream or retrieving it from there. However, if the programmer believes that he will do the job better, he can do it. 

Further, the SRC Modula-3 implementation includes the Trestle and VBTKit libraries. Trestle has already been mentioned in connection with the discussion of Modula-3's multithreading capabilities. Its use greatly facilitates the design of the user graphical interface. But for the Windows programmer, this library has a very significant "but": it emulates the X interface of the Unix world! That is, the application does not look "native" to the Windows user. True, no one bothers to rework this library accordingly... 

VBTKit is a higher-level tool built on top of Trestle. On color displays, it provides 3D interface objects (buttons, menus, dialogs, etc.) in the Motif style. 

The FormsVBT library, in turn, is built on top of the VBTKit. FormsVBT is a user interface management system. In essence, it is a simple language for describing the layout (layout) of the user interface and an event interpreter for this language. In addition, the library allows the programmer to create his own event handling procedures (callbacks). In other words, FormsVBT provides the syntax for describing the GUI, and event handlers provide its semantics. 

The FormsEdit library is based on FormsVBT. It provides an uncomplicated interactive graphical interface for prototyping user interfaces. As you might expect, it also accepts and displays specifications written in the FormsVBT language. 

The M3TK library is one of the tools that neither I, nor, probably, you have ever encountered in "this life". It provides the means to create language-dependent tools. For example, you can use it to develop software for managing the source code of a project's programs. At the heart of the M3TK is the concept of an abstract syntax tree. 

In other words, M3TK provides a metaprogramming system for Modula-3. The library has a complete set of tools for parsing language source texts, generating a syntax tree and manipulating it. Therefore, Modula-3-specific packages can be built quickly and with relative ease. In fact, the network object generator, which will be discussed below, was built using M3TK. 

It is easy to estimate how widespread networked distributed systems were in the 90s. However, most languages ​​have little or no support for distributed programming. Almost all applications are built directly on top of sockets, using simple streaming facilities (pipes) or using the RPC interface. The problem is that these tools are systemic and are poorly integrated into the language, which cannot but create serious inconveniences when creating a distributed application with an integral architecture. 

Network Objects in the SRC implementation allow you to export objects in the understanding of Modula-3 across the boundaries of address spaces on one or more computers. The executable program does not have the means to determine where the real object with which it works is located: in its own address space, or it was created and resides in a different address space on a remote computer. It is a very powerful tool for building distributed applications. 

To turn a Modula-3 object into a network object, it must inherit (directly or indirectly) from the 'NetObj.T' type. The object should not contain data fields. The interface containing the description of the object is then processed by a special tool - the generator of the network object, which generates all the code needed to work out the network interactions. That's all it takes to send an object over the network. Quite simple. 

On top of the network objects, two interesting systems are built that are included in the SRC Modula-3 kit. The first is the implementation of the Obliq language. Obliq is an object-oriented, distributed-scoping scripting language. Once the Obliq object is created, you can transfer it to another computer. The second system is Visual Obliq. You can call it "Distributed Visual Basic for Modula-3". The system includes an interactive graphical application builder. It is a very powerful tool for prototyping and building distributed applications. 

And finally, I will give the opinion of a group of professional developers who have worked with C++ since version 1.2 - they think Modula-3 is a great language with great libraries! According to them, much less intellectual effort is required to develop programs and almost always it is possible to find code in libraries that can serve as a good basis for implementing your own procedures. 

When writing this article, materials from Digital Equipment Corporation Systems Research Center were used. 

Evgeny Shcherbatyuk

Tags:
Hubs:
Rating0
Comments0

Articles