Previous Table of Contents Next


The new type has a total of three operations, each in a different category:

  Set_Name, which it inherits unchanged from its parent. That is, I have implicitly declared a new operation
   procedure Set_Name(I: in out Instrument; S: in String);
  Set_Value, a new operation specific to Speedometer
  Display_Value, a Speedometer-specific operation that overrides the corresponding Instrument one

Similarly, the type Gauge, another extension of Instrument, is declared (lines 12-18) with one overriding operation and one inherited one. Now Graphic_Gauge is declared (lines 20-24) as an extension of Gauge. I now have a three-level hierarchy of types, namely Instrument, Gauge, and Graphic_Gauge.

HB.Instruments.Clocks introduces two more instrument types.

    1 package HB.Instruments.Clocks is
    2
    3   subtype Sixty is Integer range 0 .. 60;
    4   subtype Twenty_Four is Integer range 0 .. 24;
    5
    6   type Clock is new Instrument with record
    7     Seconds : Sixty := 0;
    8     Minutes : Sixty := 0;
    9     Hours : Twenty_Four := 0;
   10   end record;
   11
   12   procedure Display_Value (C : Clock);
   13   procedure Init (C : in out Clock;
   14                   H : Twenty_Four := 0;
   15                   M, S : Sixty := 0);
   16
   17   procedure Increment(C: in out Clock; Inc: Integer :=1);
   18
   19   type Chronometer is new Clock with null record;
   20
   21   procedure Display_Value (C : Chronometer);
   22
   23 end HB.Instruments.Clocks;

Clock extends Instrument. Its operations are Set_Name (inherited from Instrument), Display_Value (overrides the one inherited from Instrument), Init (new primitive), and Increment (new primitive).

Now Chronometer extends Clock. The construct with null record satisfies a rule that a type derived from a tagged type must have an extension, even if no new components are added.

Chronometer has the following operations:

  Set_Name, inherited from Clock but declared higher, with Instrument
  Display_Value, overriding the one inherited from Clock
  Init and Increment, inherited from Clock

That an extensible type has three kinds of primitive operations—inherited from somewhere above it, overriding inherited ones, and new ones—is not unique to Ada, but inherent in the nature of OOP with inheritance, sometimes called “classification-first design.”

For large “industrial-strength” type hierarchies, where each object can have many applicable operations, this can lead to real confusion for programmers and for the readers of their programs. For both groups, an important question is “Given an object of type T, exactly which operations are applicable to it?” Keeping a mental list of these operations is difficult: Some operations—new and overriding—are declared in the same place with the type, but inherited operations could have been declared anywhere above this type in the hierarchy. In comparison, using classical packages—sometimes called “composition-first design”—results in all operations of a given type being declared with the type.

The software industry has recognized that here, as almost everywhere in computing, there is no “free lunch” in OOP: There is an important tradeoff between the flexibility of classification-first design and the ease of understanding and maintainability in composition-first design. Systems of nontrivial size will probably use both paradigms. Ada supports both paradigms equally well, so the language need not drive the design.

One might ask why Ada requires the use of tagged to indicate an extensible type, that is, why all types—or, at least, all record types—are not potentially extensible. The answer is that extensible types require additional overhead: space for an internal tag, time to distinguish at runtime which of several (potentially many) derived types a given value has, and so forth. Requiring tagged supports a key Ada principle: “You don’t have to pay for features you don’t use.”

If a type is not tagged, there will not be the associated overhead. The compiler will not generate such overhead; the human developer is assured that there will be none. This is very important to developers of real-time systems, to whom knowledge of sources of potential runtime overhead is critical. Finally, compiling an Ada 83 program with an Ada 95 compiler produces no new overhead; an Ada 83 program cannot have tagged types because these do not exist in Ada 83!


Previous Table of Contents Next