Ada: OOP
Ada's object-oriented model is built on tagged records: types that carry a hidden tag enabling dynamic dispatch. It is deliberately explicit — inheritance is written new ... with, dispatching calls are visible as class-wide operations, and interfaces are separate from state. You always know where the dynamic behavior is.
Tagged Types and Inheritance
tagged marks a record as extensible. A descendant extends it with new ... with record, inheriting fields and primitive operations:
package Shapes is
type Shape is tagged record
X, Y : Float; -- position shared by all shapes
end record;
function Area (S : in Shape) return Float is (0.0); -- a primitive operation
type Circle is new Shape with record
Radius : Float;
end record;
overriding
function Area (C : in Circle) return Float is (3.14159 * C.Radius ** 2);
end Shapes;
overriding is optional but strongly recommended: it makes the compiler confirm you actually overrode something — a typo in the name or profile becomes an error instead of a silent new subprogram.
Class-Wide Polymorphism
Shape'Class is the class-wide type: any type in the family. A dispatching call through it picks the concrete Area at run time:
with Ada.Text_IO; use Ada.Text_IO;
with Shapes; use Shapes;
procedure Dispatch_Demo is
S : Shape'Class := Circle'(X | Y => 0.0, Radius => 2.0);
begin
-- the compiler does not know which Area runs; the tag decides:
Put_Line (Float'Image (Area (S))); -- dispatches to Circle.Area: 12.57
end Dispatch_Demo;
The rule: a class-wide value can hold any family member, but must be initialized (the tag is fixed at creation); a class-wide parameter accepts any family member passed in.
Abstract Types and Interfaces
abstract types have no objects — only descendants. interface types declare contracts without any state at all:
package Render is
type Drawable is interface;
procedure Draw (D : in Drawable) is abstract;
-- every descendant MUST provide Draw
type Shape is abstract tagged null record; -- abstract base
function Area (S : in Shape) return Float is abstract;
end Render;
declare
type Square is new Render.Shape and Render.Drawable with record
Side : Float;
end record;
-- Square must implement both Area and Draw — or itself be abstract
begin
null;
end;
Interfaces multiply: a type can implement several (new A and B and C) but extend only one tagged parent — the classic single-inheritance, multi-interface compromise, statically checked.
Where Ada OOP Differs
- Subprograms live outside the type. Operations are primitive to a type by being declared in the same package — there is no
this, no method table syntax, no forced encapsulation of state with behavior in one construct. - Dispatch is opt-in. A call through a specific type is bound at compile time; only class-wide calls dispatch. Reading the code tells you which you get.
- No hidden hierarchy. Every ancestor is written in the type declaration and resolvable through the source.
Next: Contracts — pre- and postconditions on all of this.