Ada: Data Types

Ada's type system is the language's core safety mechanism. Every value has exactly one type, distinct types never mix implicitly, and you can give a type a range of valid values that the compiler and run time both enforce. This page covers scalar types, subtypes, ranges, and derived types.

The Type Family

All Ada types fall into three families. The diagram is worth memorizing — every construct you meet later is somewhere on it:

The Ada type family: scalar, composite, and access types
Fig. 1 — Scalar types hold one value; composite types hold many; access types reference values.
FamilyPredefined typesTypical use
Integer typesInteger, Natural, PositiveCounts, indices, loop variables
Real typesFloat, Long_Float, fixed pointMeasurements, physical quantities
EnumerationBoolean, Character, user-definedStates, modes, named choices
CompositeString, arrays, recordStructured data
Accesstyped referencesLinked structures, dynamic objects

Defining Your Own Types

The predefined Integer is an implementation-defined size — fine for counts, useless for domain safety. Ada's range creates a new type that accepts only valid values:

with Ada.Text_IO; use Ada.Text_IO;

procedure Type_Demo is
   -- A new integer type: values outside 1..31 are invalid by definition
   type Day_Of_Month is range 1 .. 31;

   -- A subtype: a restricted view of an existing type (no new checks beyond the range)
   subtype Work_Hours is Integer range 0 .. 40;

   Day    : Day_Of_Month := 15;
   Hours  : Work_Hours   := 38;
   Plain  : Integer      := 42;
begin
   --  Day := 32;           -- compile error: 32 is not in 1 .. 31
   --  Hours := Day;        -- compile error: distinct types never mix
   Plain  := Hours;        -- OK: a subtype IS its base type
   Put_Line ("day " & Day_Of_Month'Image (Day));
end Type_Demo;

The distinction between type and subtype is central:

  • type Day_Of_Month is range 1 .. 31; creates a new type — incompatible with Integer and with every other type, even one with the same range. Passing a bare Integer where a Day_Of_Month is expected is a compile error.
  • subtype Work_Hours is Integer range 0 .. 40; is still Integer — fully interchangeable, but values are checked against 0..40 on assignment.

Rule of thumb: type for meaning, subtype for validation. A Meters type stops you from adding meters to seconds; a Percent subtype stops any integer above 100.

Derived Types

new derives a type from an existing one, inheriting its operations — the foundation for domain models that still reuse arithmetic:

procedure Derived_Demo is
   type Meters   is new Integer range 0 .. 10_000;  -- inherits +, -, =
   type Seconds  is new Integer range 0 .. 3_600;   -- same base, different type

   Distance : Meters  := 500;
   Elapsed  : Seconds := 30;
begin
   --  Distance := Distance + Elapsed;   -- compile error: meters and seconds
   --  ...deliberately refuse to mix. The fix is an explicit conversion:
   Distance := Distance + Meters (Elapsed);  -- legal, but you wrote the intent
   null;
end Derived_Demo;

Explicit conversion (Meters (Elapsed)) is the only bridge between incompatible types — the compiler checks the value against the target range, and a violation raises Constraint_Error rather than silently truncating.

Attributes

Attributes are properties queried with a tick: they replace almost every "how big is X" question that C answers with headers or guesswork:

with Ada.Text_IO; use Ada.Text_IO;

procedure Attribute_Demo is
   type Pressure is range 0 .. 4000;
   P : Pressure := Pressure'First;   -- the smallest valid value
begin
   Put_Line ("first : " & Pressure'Image (P));
   Put_Line ("last  : " & Pressure'Image (Pressure'Last));
   Put_Line ("size  : " & Integer'Image (Pressure'Size) & " bits");
   Put_Line ("image : " & Boolean'Image (P = 0));   -- TRUE
end Attribute_Demo;

'First, 'Last, 'Range, 'Length, 'Image, and 'Value are the six you will use daily. 'Value parses text back into the type — and raises Constraint_Error on garbage instead of returning nonsense.

Checked at Run Time Too

Range checks do not stop at compile time. A computed value that leaves the range raises Constraint_Error at the point of the bad assignment:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Exceptions; use Ada.Exceptions;

procedure Overflow_Demo is
   subtype Percent is Integer range 0 .. 100;
begin
   begin
      declare
         P : Percent := 90;
      begin
         P := P + 20;   -- 110 — out of range: raised HERE, at the assignment
         Put_Line ("never printed");
      end;
   exception
      when E : Constraint_Error =>
         Put_Line ("caught: " & Exception_Message (E));
   end;
end Overflow_Demo;

In C this code silently wraps or, worse, is undefined. In Ada the failure is a named, catchable event at the exact line of the error. Next: Expressions.