Records, Aggregates & Delta Aggregates
Declare record types with default component values, build positional and named aggregates (with others => <>), select nested components, compare records with predefined equality, copy-and-modify with Ada 2022 delta aggregates, and combine arrays with records.
A record groups named components of possibly different types into one value. Records are copied by assignment, compared with =, passed to subprograms and nested freely. Ada's aggregate rules require every component to be given a value (or explicitly defaulted), so you can never forget to initialize a field when you build a record value.
Declaring and Using Records
'type Date is record Day : Integer range 1 .. 31; Month : Month_Number; Year : Integer; end record;' declares a record. Components are selected with dot notation: D.Day, E.Born.Year. Components may carry their own constraints (Day : Integer range 1 .. 31) which are checked on every assignment, and default values (Salary : Natural := 50_000) applied when an object is declared without an initial value. A record with no components is written 'null record'.
• type R is record ... end record; — or is null record; • Dot notation for components, including nested records. • Component defaults apply to declarations without initial values. • Component constraints are checked (Constraint_Error).
Aggregates
Record aggregates use parentheses (square brackets are only for arrays). Positional: (10, 12, 1815). Named: (Day => 23, Month => 6, Year => 1912) — preferred for clarity and robustness when components are reordered. 'others => <>' uses the declared defaults for remaining components; Component => <> does so for one component. Every component must be covered exactly once, so adding a component to the type makes all incomplete aggregates fail to compile — another place where the compiler points you at code to update.
An Ada 2022 delta aggregate '(Alan with delta Active => True, Salary => 80_000)' produces a copy of an existing value with only the listed components changed — ideal in postconditions ('Post => P = (P'Old with delta X => 0)') and for immutable-style updates. The original value is untouched.
• Positional (1, 2, 3) or named (A => 1, B => 2). • others => <> uses defaults; all components must be covered. • Delta aggregate: (Base with delta C => V) — copy with changes.
Equality, Arrays of Records
Predefined = compares all components (for String components that means exact equal contents and length). You can redefine "=" for a record type when logical equality differs. Arrays of records are the standard way to hold tables of data; a for ... of loop gives direct access to each record.
• = compares every component; redefine "=" if needed. • array (...) of Record — tables of structured data.
Code Examples
Ada_L gets Salary 50_000 and Active True from the defaults (others => <>). The delta aggregate prints a modified copy of Alan while Alan stays inactive. The last block computes day 10 + 22 = 32, violating the component range 1 .. 31. Output: Ada born 10/ 12/ 1815, salary 50000, active TRUE Alan born 23/ 6/ 1912, salary 72000, active FALSE Ada born 10/ 12/ 1816, salary 55000, active TRUE Alan born 23/ 6/ 1912, salary 80000, active TRUE Alan itself is unchanged, active = FALSE Birthday = (10, 12, 1815)? TRUE Active: Ada Active: Grace Day := 32 raised Constraint_Error
D: Date:= (Day => 1, Month => 1); -- Year missing D: Date:= (Day => 1, Month => 1, Year => 2024);
Aggregates must give every component (use others => <> only for components with defaults).
P:= [X => 1.0, Y => 2.0]; -- record P:= (X => 1.0, Y => 2.0);
Square brackets are for array aggregates; records use parentheses.
Tmp := Alan; Tmp.Active := True; Show (Tmp);
Show ((Alan with delta Active => True));
A delta aggregate expresses copy-and-modify directly.
What does 'others => <>' in a record aggregate mean? A. Leave components uninitialized B. Use the declared default values C. Set them to zero D. Compile error
B. Use the components' default expressions (or default-initialization).
Does (Alan with delta Active => True) modify Alan? A. Yes B. No, it produces a new value C. Only if Alan is a variable D. Only in Ada 2012
B. It produces a modified copy (verified: Alan remains inactive).
Which brackets are used for record aggregates in Ada 2022? A. [ ] B. () C. { } D. < > B. () — [ ] is for arrays.
How is predefined '=' defined for records? A. Reference equality B. Component-wise equality C. Not defined D. Compares only the first component Trace the records example.
Real output: Ada born 10/ 12/ 1815, salary 50000, active TRUE Alan born 23/ 6/ 1912, salary 72000, active FALSE Ada born 10/ 12/ 1816, salary 55000, active TRUE Alan born 23/ 6/ 1912, salary 80000, active TRUE Alan itself is unchanged, active = FALSE Birthday = (10, 12, 1815)? TRUE Active: Ada Active: Grace Day := 32 raised Constraint_Error
with Ada.Text_IO; use Ada.Text_IO;
procedure L23_Records is
type Month_Number is range 1 .. 12;
type Date is record
Day : Integer range 1 .. 31;
Month : Month_Number;
Year : Integer;
end record;
-- Components may have default values
type Employee is record
Name : String (1 .. 8);
Born : Date; -- nested record
Salary : Natural := 50_000;
Active : Boolean := True;
end record;
type Team is array (1 .. 3) of Employee;
function Image (D : Date) return String is
(Integer'Image (D.Day) & "/" & Month_Number'Image (D.Month) & "/" & Integer'Image (D.Year));
procedure Show (E : Employee) is
begin
Put_Line (E.Name & " born" & Image (E.Born) & ", salary" & Natural'Image (E.Salary)
& ", active " & Boolean'Image (E.Active));
end Show;
Birthday : constant Date := (10, 12, 1815); -- positional
Ada_L : Employee := (Name => "Ada ", Born => Birthday, others => <>); -- defaults
Alan : constant Employee :=
(Name => "Alan ", Born => (Day => 23, Month => 6, Year => 1912),
Salary => 72_000, Active => False)
…