Previous Table of Contents Next


11.2.3.7. Exceptions

An exception is a control construct that exits many scopes at once. Raising an exception exits active scopes repeatedly until a handler is found for the exception and transfers control to the handler. If there is no handler, the computation terminates in some system-dependent way—for example, by entering the debugger.

There are many arguments for and against exceptions, most of which revolve around inconclusive issues of style and taste. One argument in their favor that has the weight of experience behind it is that exceptions are a good way to handle any runtime error that is usually, but not necessarily, fatal. If exceptions are not available, each procedure that might encounter a runtime error must return an additional code to the caller to identify whether an error has occurred. This can be clumsy and has the practical drawback that even careful programmers may inadvertently omit the test for the error return code. The frequency with which returned error codes are ignored has become something of a standing joke in the UNIX/C world. Raising an exception is more robust because it stops the program unless there is an explicit handler for it.

11.2.3.8. Type System

Like all languages in the Algol family, Modula-3 is strongly typed. The basic idea of strong typing is to partition the value space into types, restrict variables to hold values of a single type, and restrict operations to apply to operands of fixed types. In actuality, strong typing is rarely so simple. For example, each of the following complications is present in at least one language of the Algol family: A variable of type [0..9] may be safely assigned to an INTEGER, but not vice versa (subtyping). Operations such as absolute value may apply both to REALs and to INTEGERs instead of to a single type (overloading). The types of literals (for example, NIL) can be ambiguous. The type of an expression may be determined by how it is used (target-typing). Type mismatches may cause automatic conversions instead of errors (as when a fractional REAL is rounded upon assignment to an integer).

We adopted several principles to make Modula-3’s type system as uniform as possible. First, there are no ambiguous types or target-typing: The type of every expression is determined by its subexpressions, not by its use. Second, there are no automatic conversions. In some cases, the representation of a value changes when it is assigned (for example, when assigning to a packed field of a record type), but the abstract value itself is transferred without change. Third, the rules for type compatibility are defined in terms of a single subtype relation. The subtype relation is required for treating objects with inheritance, but it is also useful for defining the type compatibility rules for conventional types.

11.2.3.9. Simplicity

In the early days of the Ada project, a general in the Ada Program Office opined that “obviously the Department of Defense is not interested in an artificially simplified language such as Pascal.” Modula-3 represents the opposite point of view. We used every artifice that we could find or invent to make the language simple.

C. A. R. Hoare has suggested that as a rule of thumb, a language is too complicated if it can’t be described precisely and readably in 50 pages. The Modula-3 committee elevated this to a design principle: We gave ourselves a “complexity budget” of 50 pages and chose the most useful features that we could accommodate within this budget. In the end, we were over budget by six lines plus the syntax equations. This policy is a bit arbitrary, but there are so many good ideas in programming language design that some kind of arbitrary budget seems necessary to keep a language from getting too complicated.

In retrospect, the features that made the cut were directed toward two main goals. Interfaces, objects, generics, and threads provide fundamental patterns of abstraction that help to structure large programs. The isolation of unsafe code, garbage collection, and exceptions help make programs safer and more robust. Of the techniques that we used to keep the language internally consistent, the most important was the definition of a clean type system based on a subtype relation. There is no special novelty in any one of these features individually, but there is simplicity and power in their combination.

11.3. Definitions

A Modula-3 program specifies a computation that acts on a sequence of digital components called locations. A variable is a set of locations that represents a mathematical value according to a convention determined by the variable’s type. If a value can be represented by some variable of type T, then we say that the value is a member of T and T contains the value.

An identifier is a symbol declared as a name for a variable, type, procedure, and so on. The region of the program over which a declaration applies is called the scope of the declaration. Scopes can be nested. The meaning of an identifier is determined by the smallest enclosing scope in which the identifier is declared.

An expression specifies a computation that produces a value or variable. Expressions that produce variables are called designators. A designator can denote either a variable or the value of that variable, depending on the context. Some designators are read-only, which means that they cannot be used in contexts that might change the value of the variable. A designator that is not read-only is called writable. Expressions whose values can be determined statically are called constant expressions; they are never designators.

A static error is an error that the implementation must detect before program execution. Violations of the language definition are static errors unless they are explicitly classified as runtime errors.

A checked runtime error is an error that the implementation must detect and report at runtime. The method for reporting such errors is implementation dependent. (If the implementation maps them into exceptions, then a program could handle these exceptions and continue.)

An unchecked runtime error is an error that is not guaranteed to be detected and can cause the subsequent behavior of the computation to be arbitrary. Unchecked runtime errors can occur only in unsafe modules.


Previous Table of Contents Next