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:

A generic package with formal parameters and two instantiations
Fig. 1 — Formal parameters declare requirements; each instantiation supplies actuals and yields a checked copy.
-- 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:

FormalCaller 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.