Ada: Generics
A generic unit is a compile-time template parameterized by types, operations, and values. Instantiation produces a specialized, fully checked copy. This is how Ada's containers work — and how you write one sorting routine that the compiler specializes for every type you use.
The Generic Unit
The generic part declares what callers must supply. Each formal declares a requirement, not a value:
-- sorting.ads — a generic package: note the "generic" keyword
generic
type Item_T is private; -- any type
with function "<" (A, B : Item_T) return Boolean; -- plus an ordering
package Sorting is
procedure Sort (Data : in out array);
-- (a real version takes the array type as a formal too — shown next)
end Sorting;
Instantiation
is new creates a concrete package. The actuals must satisfy every formal — the compiler checks that Integer is a type and "<" exists for it:
with Sorting;
procedure Generic_Demo is
package Int_Sorting is new Sorting (Item_T => Integer, "<" => "<");
package Str_Sorting is new Sorting (Item_T => String, "<" => "<");
begin
null; -- Int_Sorting and Str_Sorting are now separate, typed packages
end Generic_Demo;
Each instantiation is a distinct unit with its own state (if any) and its own compiled code. Two instantiations of the same generic do not share globals — an important property for parallel systems.
Formal Kinds
The formal part is where Ada generics get precise. Each declaration names what the caller may pass:
| Formal | Caller must supply |
|---|---|
type T is private; | any type; body may assign/compare |
type T is range <>; | any integer type (operations checked) |
type T is (abstract tagged); | a tagged type (for OOP generics) |
type T is array (Index) of Element; | an array type with those components |
with function F (...) return ...; | an actual subprogram with that profile |
N : Integer; | a value (e.g. a buffer size) |
Contrast with C++ templates: generics are checked at declaration time — the generic body must compile against its formals alone. A template body, by contrast, is only checked when instantiated. Ada's rule finds errors in the template itself, before anyone instantiates it.
A Complete Generic
A generic function swapping the element type, with the array type as a formal — the shape the standard containers follow:
generic
type Index_T is (<>); -- any discrete type
type Item_T is private; -- any element type
type Array_T is array (Index_T range <>) of Item_T;
function Max_Of (Data : Array_T) return Item_T;
function Max_Of (Data : Array_T) return Item_T is
Best : Item_T := Data (Data'First);
begin
for I in Data'Range loop
if Data (I) > Best then
Best := Data (I);
end if;
end loop;
return Best;
end Max_Of;
Instantiated for (1 .. 5) of Float or for an enum-indexed record array, the same body works — bounds come from the array itself. Next: Visibility.