Ada: Packages

A package is Ada's unit of modularization: a specification that states what it offers, and a body that implements it. Clients compile against the specification alone, so the implementation can change — even completely — without recompiling a single client. This is the chapter the whole language is organized around.

Spec and Body

Two files, two roles. The specification (.ads) is the contract; the body (.adb) is the implementation:

Package specification, private part, body, and child package relationships
Fig. 1 — The visible part is the API; the private part hides type internals; the body implements; children see more.
-- geometry.ads — the specification: what clients see
package Geometry is
   type Square is private;                      -- hidden internals
   function Make (Side : Float) return Square;
   function Area (S : Square) return Float;
private
   type Square is record Side : Float; end record;   -- visible to children only
end Geometry;
-- geometry.adb — the body: how it works
package body Geometry is

   function Make (Side : Float) return Square is
     ((Side => Side));                     -- record aggregate as a body

   function Area (S : Square) return Float is
     (S.Side * S.Side);

begin
   null;   -- optional elaboration code runs once at start-up
end Geometry;

Using a Package

A client names the package with with and may use it to drop the prefix. Every reference is still resolved at compile time — there is no dynamic dispatch hiding here:

with Geometry;              -- name the dependency
--  with Geometry; use Geometry;   -- with use: Area (S) instead of Geometry.Area (S)

procedure Client is
   S : Geometry.Square := Geometry.Make (2.0);
begin
   null;   -- Geometry.Area (S) = 4.0
end Client;

Style note: many teams reserve use for operators and enumeration literals, keeping type names prefixed — it makes provenance obvious at every call site.

Private Types

type Square is private; declares a type whose clients know exists but cannot inspect or construct directly. The full declaration appears under private in the same spec. This is Ada's encapsulation workhorse:

  • Clients can declare Square variables, assign, and compare them — but not read or write Side directly.
  • The only way in is through the package's functions: Make can enforce invariants (side > 0) that every object satisfies.
  • Change the record to hold a diagonal instead — no client recompiles, because nothing in the visible part changed.

Child Packages

Large units grow as children: Geometry.Shapes lives in its own spec/body pair, has full access to Geometry's private part, and adds capabilities without touching the parent:

-- geometry-shapes.ads
package Geometry.Shapes is
   function Perimeter (S : Geometry.Square) return Float;
end Geometry.Shapes;

Children solve the "library extension" problem: the standard library is built this way (Ada.Text_IO is a child of Ada), and your own hierarchies will use the same pattern. Next: Exceptions.