Previous | Table of Contents | Next |
Even in its original 1983-standard form, Ada contained most of the important ingredients for object-oriented software development.
First, packages, together with private types, provide a strong encapsulation mechanism that gives the programmer tight control over the visibility, state, and behavior of objects.
Further, two kinds of polymorphism are provided: The generic package facility gives the ability to parameterize packages with respect to types and operations, and overloading allows you to use the same name for a number of different operations with different parameter profiles.
Finally, inheritance is provided through type derivation. In Ada 83 you can write, for example,
package Credit_Cards is type Credit_Card is private; procedure P1 (X: in Credit_Card; ...); procedure P2 (X: in out Credit_Card; ...); ... private type Credit_Card is... end Credit_Cards;
and then derive new types such as
type Personal_Card is new Credit_Card; type Business_Card is new Credit_Card;
These three types form a type hierarchy. Personal_Card and Business_Card inherit the structural details of Credit_Card and also the operations P1 and P2. The three types are not compatible with each other but can be explicitly converted so that if you have
C: Credit_Card; P: Personal_Card; B: Business_Card;
then
C := B; P := B; B := C;
are all disallowed, but
C := Credit_Card(B); P := Personal_Card(B); B := Business_Card(P);
are all valid. Furthermore,
P1(C); P1(P); P1(B);
are all valid and the compiler can determine which one to dispatch for each object.
The common characteristic in generics, overloading, and Ada 83 type derivation is that given an object and a set of similarly-named operations, the operation to be executed can be determined essentially at compilation time.
Why, then, did the Ada 83 designers not go the rest of the distance and provide for the kind of inheritance and runtime polymorphism programmers have come to expect for object-oriented programming (OOP)? There is considerable folklore about the discussions at the time, but not much available written record on this issue. It is a matter of fact, though, that Ichbiah and his team were well aware of, and considered carefully, the costs and benefits of these features. The Ada 83 Rationale says just this on the subject (in the introduction to Chapter 9):
Facilities for modularization have appeared in many languages. Some of themsuch as Simula, Clu, and Alphardprovide dynamic facilities which may entail large run-time overhead. The facility provided in Ada is more staticin the spirit of previous solutions offered in Lis, Euclid, Mesa, and Modula. At the same time it retains the best aspects of solutions in earlier languages such as Fortran and Jovial.
I conclude that given the slow speed and memory constraints of the computers of that period, the main concern was about runtime costs. The Ada 83 designerswho did most of their work in the late 1970sprovided the hooks for full OOP but left it to the second generation of designerswho produced Ada 95 beginning in 1988, when far more powerful hardware was common and other OO languages had become popularto implement it fully.
The Ada 95 designers recognized the strength of the existing Ada 83 type declaration and derivation facilities and so determined that extending the type facilitynot, for example, attempting to build C++-style classes by providing a package typewas the proper way to provide for inheritance. Indeed, in this way a large increase in expressive power for OOP is achieved with remarkably little additional syntax.
10.4.3.1. Classical Polymorphic Types: Variant Records
Consider a typical Ada application in the software controlling the instrument cluster in an automobile. There are several kinds of instruments; all have some common features but each is also different from the others. A classical solutionin Ada 83 and earlier languagesis to represent the instrument type as a variant record, sometimes called a discriminated record or, more formally, discriminated union. In Ada, given some basic types
subtype Speeds is Integer range 0 .. 85; -- mph subtype Percent is Integer range 0 .. 100; type InstrumentKinds is (Speedometer, Gauge, Graphic_Gauge);
the variant record type might look like
type Instrument (Kind: InstrumentKinds) is record Name: String(1 .. 14):= ; case Kind is when Speedometer => SpeedValue: Speeds; when Gauge => GaugeValue: Percent; when Graphic_Gauge => Reading: Percent; Size : Integer:= 20; Fill : Character:= *; Empty: Character:= .; end case; end record;
Previous | Table of Contents | Next |