Introducing Modula-3

One of the main tenets of the Unix philosophy is using the right tool for the right job. Here is a well-crafted tool well-suited for many large jobs that are diffficult to do well in C.

Suppose you want to develop a large,
complex application for Linox. The application is going to be
multiprocess, perhaps distributed, and definitely has to have a
GUI. You want to build this application fairly quickly, and you
want it to be relatively bug free.

One of the first questions you might ask yourself is “What
programming language and environment should I use?” C might be a
good choice, but probably not for this project. It doesn't scale as
well as you'd like, and the tools for doing
multiprocess/distributed programming for C just aren't there. You
might consider C++, but the language is fairly complex. Also, you
and others have discovered from past experience that a fair amount
of time goes into debugging subtle memory management
problems.

There is an alternative, the Modula-3 programming system from
Digital Equipment Corporation's Systems Research Center (SRC).
Modula-3 is a modern, modular, object-oriented language. The
language features garbage collection, exception handling, run-time
typing, generics, and support for multithreaded applications. The
SRC implementation of this language features a native-code
compiler; an incremental, generational, conservative, multithreaded
garbage collector (whew!); a minimal recompilation system; a
debugger; a rich set of libraries; support for building distributed
applications; a distributed objectoriented scripting language; and,
finally, a graphical user interface builder for distributed
applications. In short, the ideal environment for the type of
application outlined above. Moreover, the system is freely
available in source form, and pre-built Linux binaries are
available as well.

The remainder of this article will touch on the pertinent
features of the language and provide an overview of the libraries
and tools.

The Basics of the Modula-3 Language

One of the principal goals for the Modula-3 language was to
be simple and comprehensible, yet suitable for building large,
robust, long-lived applications and systems. The language design
process was one of consolidation and not innovation; that is, the
goal was to consolidate ideas from several different languages,
ideas that had proven useful for building large sophisticated
systems.

Features of Modula-3

You can think of Modula-3 as starting with Pascal and
re-inventing it to make it suitable for real systems development.
Beginning with a Pascal-like base, features were integrated that
were deemed necessary for writing real applications. These features
fall roughly into two areas: those which make the language more
suitable for structuring large systems, and those which make it
possible to do “machine-level” programming. Real applications need
both of these.

Supporting Large Systems Development

There are several features in Modula-3 that support
structuring of large systems. First is the separation of interface
from implementation. This allows for system evolution as
implementations evolve without affecting the clients of those
interfaces; no one is dependent on how you implement something,
only what you implement. As long as the what stays constant, the
how can change as much as is needed.

Secondly, it provides a simple single-inheritance object
system. There is a fair amount of controversy over what the proper
model for multiple inheritance (MI) is. I have built systems that
use multiple-inheritance extensively and have implemented
programming environments for a language that supports MI.
Experience has taught me that MI can complicate a language
tremendously (both conceptually and in terms of implementation) and
can also complicate applications.

Modula-3 has a particularly simple definition of an object.
In Modula-3, an object is a record on the heap with an associated
method suite. The data fields of the object define the state and
the method suite defines the behavior. The Modula-3 language allows
the state of an object to be hidden in an implementation module
with only the behavior visible in the interface. This is different
than C++ where a class definition lists both the member data and
member function. The C++ model reveals what is essentially private
information (namely the state) to the entire world. With Modula-3
objects, what should be private can really be private.

One of the most important features in Modula-3 is garbage
collection. Garbage collection really enables robust, long-lived
systems. Without garbage collection, you need to define conventions
about who owns a piece of storage. For instance, if I pass you a
pointer to a structure, are you allowed to store that pointer
somewhere? If so, who is responsible for de-allocating the
structure in the future? You or me? Programmers wind up adopting
such conventions as the explicit use of reference counts to
determine when it is safe to deallocate storage. Unfortunately,
programmers are not very good about following conventions. The net
result is that programs develop storage leaks or the same piece of
storage is mistakenly used for two different purposes. Also, in
error situations, it may be difficult to free the storage. In C, a
longjmp may cause storage to be lost if the procedure being unwound
doesn't get a chance to clean up. Exception handling in C++ has the
same problems. In general, it is very difficult to manually reclaim
storage in the face of failure. Having garbage collection in the
language removes all of these problems. Better yet, the garbage
collector that is provided with the SRC implementation of Modula-3
has excellent performance. It is the result of several years of
production use and tuning.

Most modern systems and applications have some flavor of
asynchrony in them. Certainly all GUI-based applications are
essentially asynchronous. Inputs to a GUI-based application are
driven by the user. Multiprocess and multi-machine applications are
essentially asynchronous as well. Given this, it is surprising that
very few languages provide any support at all for managing
concurrency. Instead, they “leave it up to the programmer”. More
often than not, programmers do this through the use of timers and
signal handlers. While this approach suffices for fairly simple
applications, it quickly falls apart as applications grow in
complexity or when an application uses two different libraries,
both of which try to implement concurrency in their own way. If you
have ever programmed with Xt or Motif, then you are aware of the
problems with nested event loops. There needs to be some standard
mechanism for concurrency.

Modula-3 provides such a standard interface for creating
threads. In addition, the language itself includes support for
managing locks. The standard libraries provided in the SRC
implementation are all thread-safe. Trestle, which is a library
providing an interface to X, is not only thread-safe, but itself
uses threads to carry out long operations in the background. With a
Trestle-based application, you can create a thread to carry out
some potentially long-running operation in response to a
mouse-button click. This thread runs in the background without
tying up the user interface. It is a lot simpler and less error
prone than trying to accomplish the same thing with signal handlers
and timers.

Generic interfaces and modules are a key to reuse. One of the
principal uses is in defining container types such as stacks,
lists, and queues. They allow container objects to be independent
of the type of entity contained. Thus, one needs to define only a
single “Table” interface that is then instantiated to provide the
needed kind of “Table”, whether an integer table, or a
floatingpoint table or some other type of table is needed. Modula-3
generics are cleaner than C++ parameterized types, but provide much
of the same flexibility.

Trending Topics

Upcoming Webinar

Getting Started with DevOps - Including New Data on IT Performance from Puppet Labs 2015 State of DevOps Report

August 27, 2015
12:00 PM CDT

DevOps represents a profound change from the way most IT departments have traditionally worked: from siloed teams and high-anxiety releases to everyone collaborating on uneventful and more frequent releases of higher-quality code. It doesn't matter how large or small an organization is, or even whether it's historically slow moving or risk averse — there are ways to adopt DevOps sanely, and get measurable results in just weeks.