Overview of Ada 2022
Jeff Cousins
Contents   Index   Search   Previous   Next 

4.5 Container aggregates

Currently, it is quite tedious to initialise a container, one has to create it as an empty container and then add elements one at a time, as in:
X : My_Set := Empty_Set;
Include (X, 1);
Include (X, 2);
Include (X, 3);
Container aggregates; generalized array aggregates (AI12-0212) adds positional container aggregates (see RM 4.3.5). These allow the above to be replaced by simply:
X : My_Set := [ 1, 2, 3 ];
Note that this uses square brackets not round brackets (parentheses). This allows the use of [ ] to indicate an empty container, analogous to "" indicating an empty string.
This is achieved using the new aspect Aggregate to indicate the appropriate function for returning an empty container of the particular container type, and also the appropriate procedure for adding an element to the particular container type.
For example:
type Set is tagged private
   with -- Ada 2012 has these
        Constant_Indexing => Constant_Reference,
        Default_Iterator      => Iterate,
        Iterator_Element    => Element_Type,
        ...  -- but this is new
        Aggregate              => (Empty               => Empty,
                                             Add_Unnamed => Include),
        ...
Originally an Empty_<Container> constant was asked for in AI12-0212, but Empty function for Container aggregates (AI12-0339) tweaked this such that now an Empty function is given instead, so as to allow a Capacity parameter for those container types that have the concept of capacity (e.g. Vectors).
Add_Unnamed requires a two parameter procedure for adding a single element. As the existing Append procedures of vectors and lists required a third, Count, parameter, Contracts for container operations (AI12-0112) added a procedure without the Count parameter (at the time called Append_One) to the Vectors container, and List containers need Append_One (AI12-0391) did the same for the Doubly_Link_Lists container. Ambiguities associated with Vector Append and container aggregates (AI12-0400) then decided it was cleaner for the new procedures to be overloadings of Append, and to remove the default for Count (of := 1) from the original Append procedures.
Iteration is also possible within the container aggregate, for example to create a set whose elements all have double the value of the corresponding elements of another set:
Doubles_Set : My_Set := [ for Item of X => Item * 2 ];
Note that this uses similar syntax to that introduced by Index parameters in array aggregates (AI12-0061) (see 7.4).
And – prepare yourself for a shock! – array aggregates are also allowed to use square brackets as an alternative to round brackets (parentheses). This is to emphasise the similarity in characteristics between containers and arrays, allow the use of [ ] for an empty array, and allow the use of positional notation for a single element array. Remember that
   Two_Array : array (1 .. 2) of Positive := (1, 2);
is allowed, but not:
   One_Array : array (1 .. 1) of Positive := (1); -- Illegal.
AI12-0212 also introduces two other kinds of container aggregates. These work similarly to the positional container aggregates already described, so we won't describe the details of defining these.
Named container aggregates allow the specification of key choices along with element values, just like a named array aggregate. For a named container aggregate, the choices can be of any type, so these aggregates are especially useful for maps, as they can give the keys and elements of a map at once. For instance, assuming an appropriate map has been defined, one could write:
   Retired : Expert_Map := ["Jeff" => True, "Randy" => False, "Tuck" => False];
Indexed container aggregates are similar to named container aggregates except that they require the key choices to be of a discrete type. In exchange for this requirement, indexed container aggregates work similarly to an array aggregate, including a compile-time check that all of the specified choices cover a single range with no duplicates. Thus, using indexed container aggregates can help eliminate errors. For instance:
   Part_Time : Employee_Vector := [1 | 3 | 5..10 => False, 2 | 4 => True]; -- OK.
   On_Vacation : Employee_Vector := [1 .. 4 | 8 .. 10 => False, 5 | 7 => True]; -- Illegal.
The second aggregate is illegal as employee #6 is not specified.
One difference between container aggregates and array aggregates is that no others choice is allowed in container aggregates. An others choice only makes sense when the number of elements in the resulting object is known at compile-time, and that is never the case for containers.
Unfortunately introducing aggregates for containers introduced an ambiguity – for the Append, Insert and Prepend operations of vectors, if the element type is a record then it is unclear whether it is an element or another vector that is being added. Ambiguities associated with Vector Append and container aggregates (AI12-0400) renames the operations for adding a vector to Append_Vector, Insert_Vector and Prepend_Vector. Note that this is a backward incompatibility; compiler vendors are strongly encouraged to ease this incompatibility by adding these newly named routines to their Ada 2012 implementations.

Contents   Index   Search   Previous   Next 
© 2021, 2022 Jeff Cousins