Rationale for Ada 2005

John Barnes
Table of Contents   Index   References   Search   Previous   Next 

4.4 Aggregates

There are important changes to aggregates in Ada 2005 which are very useful in a number of contexts. These were triggered by the changes to the rules for limited types which are described in the next section, but it is convenient to first consider aggregates separately.
The main change is that the box notation <> is now permitted as the value in a named aggregate. The meaning is that the component of the aggregate takes the default value if there is one.
So if we have a record type such as 
type RT is
   record
      A: Integer := 7;
      B: access Integer;
      C: Float;
   end record;
then if we write 
X: RT := (A => <>, B => <>, C => <>);
then X.A has the value 7, X.B has the value null and X.C is undefined. So the default value is that given in the record type declaration or, in the absence of such an explicit default value, it is the default value for the type. If there is no explicit default value and the type does not have one either then the value is simply undefined as usual.
The above example could be abbreviated to 
X: RT := (others => <>);
The obvious combinations are allowed 
(A => <>, B => An_Integer'Access, C => 2.5)
(A => 3, others => <>)
(A => 3, B | C => <>)
The last two are the same. There is a rule in Ada 95 that if several record components in an aggregate are given the same expression using a | then they have to be of the same type. This does not apply in the case of <> because no typed expression is involved.
The <> notation is not permitted with positional notation. So we cannot write
(3, <>, 2.5)    -- illegal
But we can mix named and positional notations in a record aggregate as usual provided the named components follow the positional ones, so the following are permitted
(3, B => <>, C => 2.5)
(3, others => <>)
A minor but important rule is that we cannot use <> for a component of an aggregate that is a discriminant if it does not have a default. Otherwise we could end up with an undefined discriminant.
The <> notation is also allowed with array aggregates. But in this case the situation is much simpler because it is not possible to give a default value for array components. Thus we might have 
P: array (1.. 1000) of Integer := (1 => 2, others => <>);
The array P has its first component set to 2 and the rest undefined. (Maybe P is going to be used to hold the first 1000 prime numbers and we have a simple algorithm to generate them which requires the first prime to be provided.) The aggregate could also be written as 
(2, others => <>)
Remember that others is permitted with a positional array aggregate provided it is at the end. But otherwise <> is not allowed with a positional array aggregate.
We can add others => <> even when there are no components left. This applies to both arrays and records.
The box notation is also useful with tasks and protected objects used as components. Consider
protected type Semaphore is ... ;
type PT is
   record
      Guard: Semaphore;
      Count: Integer;
      Finished: Boolean := False;
   end record;
As explained in the next section, we can now use an aggregate to initialize an object of a limited type. Although we cannot give an explicit initial value for a Semaphore we would still like to use an aggregate to get a coverage check. So we can write 
X: PT := (Guard => <>, Count => 0, Finished => <>);
Note that although we can use <> to stand for the value of a component of a protected type in a record we cannot use it for a protected object standing alone. 
Sema: Semaphore := <>;    -- illegal
The reason is that there is no need since we have no coverage check to concern us and there could be no other reason for doing it anyway.
Similarly we can use <> with a component of a private type as in 
type Secret is private;
type Visible is
   record
      A: Integer;
      S: Secret;
   end record;
X: Visible := (A => 77; S => <>);
but not when standing alone 
S: Secret := <>;    -- illegal
It would not have any purpose because such a variable will take any default value anyway.
We conclude by mentioning a small point for the language lawyer. Consider 
function F return Integer;
type T is
   record
      A: Integer := F;
      B: Integer := 3;
   end record;
Writing 
X: T := (A => 5, others => <>);    -- does not call F
is not quite the same as 
X: T;    -- calls F
...
X.A := 5;  X.B := 3;
In the first case the function F is not called whereas in the second case it is called when X is declared in order to default initialize X.A. If it had a nasty side effect then this could matter. But then programmers should not use nasty side effects anyway.

Table of Contents   Index   References   Search   Previous   Next 
© 2005, 2006 John Barnes Informatics.
Sponsored in part by:
The Ada Resource Association and its member companies: ARA Members AdaCore Polyspace Technologies Praxis Critical Systems IBM Rational Sofcheck and   Ada-Europe:
Ada-Europe