Annotated Ada Reference Manual (Ada 202y Draft 1)Legal Information
Contents   Index   References   Search   Previous   Next 

4.3.5 Container Aggregates

1/5
{AI12-0212-1} In a container_aggregate, values are specified for elements of a container; for a positional_container_aggregate, the elements are given sequentially; for a named_container_aggregate, the elements are specified by a sequence of key/value pairs, or using an iterator. The Aggregate aspect of the type of the aggregate determines how the elements are combined to form the container.
1.a/5
Term entry: container aggregate — construct used to define a value of a type that represents a collection of elements, by explicitly specifying the elements in the collection
2/5
{AI12-0212-1} For a type other than an array type, the following type-related operational aspect may be specified:
3/5
Aggregate
This aspect is an aggregate of the form:
3.a/5
Aspect Description for Aggregate: Mechanism to define user-defined aggregates.
4/5
   (Empty => name[,
    Add_Named => procedure_name][,
    Add_Unnamed => procedure_name][,
    New_Indexed => function_name,
    Assign_Indexed => procedure_name])
4.a/5
Ramification: {AI22-0020-1} As this aspect is described with syntax, it has to be given exactly as specified here. That means it cannot be given as a positional aggregate, nor can the order of the elements be changed. For instance, New_Indexed always has to occur before Assign_Indexed, and Empty always has to be first. 
5/5
{AI12-0212-1} The type for which this aspect is specified is known as the container type of the Aggregate aspect. A procedure_name shall be specified for at least one of Add_Named, Add_Unnamed, or Assign_Indexed. If Add_Named is specified, neither Add_Unnamed nor Assign_Indexed shall be specified. Either both or neither of New_Indexed and Assign_Indexed shall be specified.

Name Resolution Rules

6/6
{AI12-0212-1} {AI22-0002-1} The name specified for Empty for an Aggregate aspect shall denote a constant of the container type, or denote exactly one function with a result type of the container type that has no parameters, or that has one in parameter of a signed integer type.
6.a/6
Reason: The parameter of In the function case, the parameter, if present, may be used to specify an initial size for the container, in anticipation of adding elements to it. For a positional aggregate, or a named aggregate that doesn't use an iterator, it will be initialized with the number of elements. For a named aggregate that uses an iterator, the implementation is permitted to estimate the number of elements that the iterator will produce, but it is not required to do so. 
7/6
{AI12-0212-1} {AI12-0437-1} {AI22-0021-1} The procedure_name specified for Add_Unnamed for an Aggregate aspect shall denote exactly one procedure that has two parameters, the first an in out parameter of the container type, and the second an in parameter whose type is of some nonlimited type, called the element type of the container type.
8/6
{AI12-0212-1} {AI12-0437-1} {AI22-0021-1} The function_name specified for New_Indexed for an Aggregate aspect shall denote exactly one function with a result type of the container type, and two parameters of the same discrete type, called with that type being the key type of the container type.
8.a/5
Reason: The New_Indexed function is used instead of Empty as the first step of creating an aggregate that is initialized using the Assign_Indexed procedure. 
9/6
{AI12-0212-1} {AI12-0437-1} {AI22-0021-1} The procedure_name specified for Add_Named or Assign_Indexed for an Aggregate aspect shall denote exactly one procedure that has three parameters, the first an in out parameter of the container type, the second an in parameter whose of a nonlimited type is called (the key type of the container type), and the third, an in parameter whose of a nonlimited type that is called the element type of the container type.

Legality Rules

10/6
{AI12-0212-1} {AI22-0002-1} If the container type of an Aggregate aspect is a private type, the full type of the container type shall not be an array type. If the container type is limited, the name specified for Empty shall denote a function rather than a constant object.
11/6
{AI12-0212-1} {AI22-0021-1} For an Aggregate aspect, neither the element type nor the key type (if any) of the container type shall be a limited type. Additionally, the key type of Assign_Indexed shall be the same type as that of the parameters of New_Indexed, and that type shall be a discrete type. If Additionally, if both Add_Unnamed and Assign_Indexed are specified, the final parameters shall be of the same type — the element type of the container type.
11.1/6
  {AI22-0021-1} None of the subprograms specified for an Aggregate aspect shall have a formal access parameter, nor an explicitly aliased formal parameter.
11.a/6
Reason: Both access parameters and explicitly aliased parameters add additional requirements on the corresponding actual parameter. These complicate the description and implementation of container aggregates with little benefit. Thus we make them illegal. This means neither the element type nor the key type (if any) of the container type can be an anonymous access type.
11.2/6
  {AI22-0032-1} If the container type T is not abstract, then none of the subprograms specified in the Aggregate aspect for T shall be abstract.
11.b/6
Ramification: Since the Aggregate aspect is nonoverridable, this rule is rechecked on any derived type that inherits an Aggregate aspect. 
11.3/6
  {AI22-0032-1} In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit.
11.c/6
Ramification: The first Legality Rule has to be rechecked in instances, if a type with an Aggregate aspect is derived from a formal private type, in case the actual type for the private type is an array type. The last Legality Rule has to be rechecked in instances, in case an actual type for an abstract formal type is not abstract. 

Static Semantics

12/5
{AI12-0212-1} {AI12-0388-1} The Aggregate aspect is nonoverridable (see 13.1.1).

Syntax

13/5
{AI12-0212-1} container_aggregate ::= 
    null_container_aggregate
  | positional_container_aggregate
  | named_container_aggregate
14/5
{AI12-0212-1} null_container_aggregate ::= '[' ']'
15/5
{AI12-0212-1} positional_container_aggregate ::= '[' expression{, expression} ']'
16/5
{AI12-0212-1} named_container_aggregate ::= '[' container_element_association_list ']'
16.a/5
Reason: {AI12-0212-1} Unlike other aggregates, container aggregates have to be surrounded with brackets rather than parentheses. Using brackets allows writing zero- and one-element positional aggregates. (Were we to use parentheses, a one-element positional aggregate would look the same as and would always be interpreted as a parenthesized expression.) Unlike the case for arrays, where it is always possible to give bounds to work around this gap, such an aggregate could not be written at all for a sequence type (such as a list) where there is no index or key to use. 
17/5
{AI12-0212-1} container_element_association_list ::= 
    container_element_association {, container_element_association}
18/5
{AI12-0212-1} container_element_association ::= 
    key_choice_list => expression
  | key_choice_list => <>
  | iterated_element_association
19/5
{AI12-0212-1} key_choice_list ::= key_choice {'|' key_choice}
20/5
{AI12-0212-1} key_choice ::= key_expression | discrete_range
21/5
{AI12-0212-1} iterated_element_association ::= 
    for loop_parameter_specificationuse key_expression] => expression
  | for iterator_specificationuse key_expression] => expression

Name Resolution Rules

22/5
{AI12-0212-1} {AI12-0437-1} The expected type for a container_aggregate shall be a single type for which the Aggregate aspect has been specified. The expected type for each expression of a container_aggregate is the element type of the expected type.
23/5
{AI12-0212-1} The expected type for a key_expression, or a discrete_range of a key_choice, is the key type of the expected type of the aggregate.

Legality Rules

24/6
{AI12-0212-1} {AI22-0018-1} The expected type for a positional_container_aggregate shall have an Aggregate aspect that includes a specification for an Add_Unnamed procedure or an Assign_Indexed procedure. The expected type for a named_container_aggregate that contains one or more iterated_element_associations with a key_expression shall have an Aggregate aspect that includes a specification for the Add_Named procedure. The expected type for a named_container_aggregate that contains one or more key_choice_lists shall have an Aggregate aspect that includes a specification for the Add_Named or Assign_Indexed procedure. [For other kinds of named_container_aggregates, there are no additional restrictions on the Aggregate aspect of the expected type. A null_container_aggregate can be of any type with an Aggregate aspect.]
25/5
{AI12-0212-1} A non-null container aggregate is called an indexed aggregate if the expected type T of the aggregate specifies an Assign_Indexed procedure in its Aggregate aspect, and either there is no Add_Unnamed procedure specified for the type, or the aggregate is a named_container_aggregate with a container_element_association that contains a key_choice_list or a loop_parameter_specification. The key type of an indexed aggregate is also called the index type of the aggregate.
26/6
{AI12-0212-1} {AI22-0018-1} A container_element_association with a <> rather than an expression, or with a key_choice that is a discrete_range, is permitted only in an indexed aggregate.
26.a/5
Reason: Only an element of an indexed aggregate can be left uninitialized, because the compiler can simply omit producing an Assign_Indexed operation for the given index. For other kinds of aggregates, there are no operations that add an element without giving it a value. We could have defined such (optional) operations, but they would have added significant complication to an already complex feature without adding much additional expressive power. Note that, to be consistent with array aggregates, we do not support specifying <> in an iterated_element_association.
27/5
{AI12-0212-1} For an iterated_element_association without a key_expression, if the aggregate is an indexed aggregate or the expected type of the aggregate specifies an Add_Named procedure in its Aggregate aspect, then the type of the loop parameter of the iterated_element_association shall be the same as the key type of the aggregate.
27.a/5
Ramification: If there is a key_expression in an iterated_element_association, it determines the key of each added key/value pair, rather than the loop parameter. But if there is no key_expression, the loop parameter itself is used as the key. 
28/5
{AI12-0212-1} {AI12-0250-1} For a named_container_aggregate that is an indexed aggregate, all container_element_associations shall contain either a key_choice_list, or a loop_parameter_specification without a key_expression or iterator_filter. Furthermore, for such an aggregate, either: 
29/5
all key_choices shall be static expressions or static ranges, and every loop_parameter_specification shall have a discrete_subtype_definition that defines a non-null static range, and the set of values of the index type covered by the key_choices and the discrete_subtype_definitions shall form a contiguous range of values with no duplications; or
30/5
there shall be exactly one container_element_association, and if it has a key_choice_list, the list shall have exactly one key_choice.
30.a/5
Reason: The above is trying to mimic the rules for named_array_aggregates, without others

Dynamic Semantics

31/5
{AI12-0212-1} The evaluation of a container_aggregate starts by creating an anonymous object A of the expected type T, initialized as follows:
32/5
if the aggregate is an indexed aggregate, from the result of a call on the New_Indexed function; the actual parameters in this call represent the lower and upper bound of the aggregate, and are determined as follows: 
33/5
if the aggregate is a positional_container_aggregate, the lower bound is the low bound of the subtype of the key parameter of the Add_Indexed procedure, and the upper bound has a position number that is the sum of the position number of the lower bound and one less than the number of expressions in the aggregate;
34/5
if the aggregate is a named_container_aggregate, the lower bound is the lowest value covered by a key_choice_list or is the low bound of a range defined by a discrete_subtype_definition of a loop_parameter_specification; the upper bound is the highest value covered by a key_choice_list or is the high bound of a range defined by a discrete_subtype_definition of a loop_parameter_specification.
35/6
{AI22-0002-1} if the aggregate is not an indexed aggregate, by assignment from the Empty constant, or from a call on the Empty function specified in the Aggregate aspect. In the case of an Empty function with a formal parameter, the actual parameter has the following value:
36/5
for a null_container_aggregate, the value zero;
37/5
for a positional_container_aggregate, the number of expressions;
38/6
This paragraph was deleted.{AI22-0018-1} for a named_container_aggregate without an iterated_element_association, the number of key_expressions;
39/6
{AI22-0018-1} for a named_container_aggregate where each every iterated_element_association, if any, contains a loop_parameter_specification, the total number of elements specified by all of the container_element_associations;
40/5
otherwise, to an implementation-defined value.
40.a/5
Implementation Note: This value ought to be an estimate for the number of elements in the aggregate, if one is available. If not, it is suggested to use the default value for the parameter if one exists, and zero otherwise. 
40.b/5
Implementation defined: The value of the parameter to Empty for some container_aggregates.
41/5
{AI12-0212-1} The evaluation then proceeds as follows:
42/5
for a null_container_aggregate, the anonymous object A is the result;
43/5
for a positional_container_aggregate of a type with a specified Add_Unnamed procedure, each expression is evaluated in an arbitrary order, and the Add_Unnamed procedure is invoked in sequence with the anonymous object A as the first parameter and the result of evaluating each expression as the second parameter, in the order of the expressions;
44/6
{AI22-0031-1} for a positional_container_aggregate that is an indexed aggregate, each expression is evaluated in an arbitrary order, and the Assign_Indexed procedure is invoked once for each expression of the aggregate in sequence with the anonymous object A as the first parameter, the key value as the second parameter, computed by starting with the low bound of the subtype of the key formal parameter of the Assign_Indexed procedure and taking the successor of this value for each successive expression of the aggregate, and the result of evaluating each expression as the third parameter;
45/5
for a named_container_aggregate for a type with an Add_Named procedure in its Aggregate aspect, the container_element_associations are evaluated in an arbitrary order: 
46/6
{AI22-0031-1} for a container_element_association with a key_choice_list, for each key_choice of the list in an arbitrary order, the key_choice is evaluated, as is the expression of the container_element_association (in an arbitrary order), and the Add_Named procedure is invoked once for each value covered by the key_choice (in an arbitrary order),, with the anonymous object A as the first parameter, the value from the key_choice as the second parameter, and the result of evaluating the expression of the container_element_association as the third parameter;
47/5
{AI12-0212-1} {AI12-0327-1} for a container_element_association with an iterated_element_association, first the iterated_element_association is elaborated, then an iteration is performed, and for each value conditionally produced by the iteration (see 5.5 and 5.5.2) the Add_Named procedure is invoked with the anonymous object A as the first parameter, the result of evaluating the expression as the third parameter, and:
48/5
if there is a key_expression, the result of evaluating the key_expression as the second parameter;
49/5
otherwise, with the loop parameter as the second parameter;
50/5
for a named_container_aggregate that is an indexed aggregate, the evaluation proceeds as above for the case of Add_Named, but with the Assign_Indexed procedure being invoked instead of Add_Named; in the case of a container_element_association with a <> rather than an expression, the corresponding call on Assign_Indexed is not performed, leaving the component as it was upon return from the New_Indexed function;
51/5
for any other named_container_aggregate, the container_element_associations (which are necessarily iterated_element_associations) are evaluated in the order given; each such evaluation comprises two steps: 
52/5
1.
the iterated_element_association is elaborated;
53/5
2.
{AI12-0212-1} {AI12-0327-1} an iteration is performed, and for each value conditionally produced by the iteration (see 5.5 and 5.5.2) the Add_Unnamed procedure is invoked, with the anonymous object A as the first parameter and the result of evaluating the expression as the second parameter.
53.a/5
Ramification: In this case, the value of the loop parameter is not directly relevant, though presumably it appears within the expression of the iterated_element_association.
54/6
{AI22-0031-1} When the above wording says that a subprogram is invoked or called, this is a subprogram call as defined in 6.4, with parameter associations as specified in the wording evaluated as defined 6.4.1. [In particular, this means that the parameters are converted to the subtype of the formal parameter (which can raise an exception — see 4.6).]

Examples

55/5
{AI12-0212-1} {AI12-0429-1} Examples of specifying the Aggregate aspect for a Set_Type, a Map_Type, and a Vector_Type:
56/5
   --  Set_Type is a set-like container type.
   type Set_Type is private
      with Aggregate => (Empty       => Empty_Set,
                         Add_Unnamed => Include);
   function Empty_Set return Set_Type;
57/5
   subtype Small_Int is Integer range -1000..1000;
58/5
   procedure Include (S : in out Set_Type; N : in Small_Int);
59/5
   --  Map_Type is a map-like container type.
   type Map_Type is private
      with Aggregate =>  (Empty     => Empty_Map,
                          Add_Named => Add_To_Map);
60/5
   procedure Add_To_Map (M     : in out Map_Type;
                         Key   : in Integer;
                         Value : in String);
61/5
   Empty_Map : constant Map_Type;
62/5
   --  Vector_Type is an extensible array-like container type.
   type Vector_Type is private
      with Aggregate => (Empty          => Empty_Vector,
                         Add_Unnamed    => Append_One,
                         New_Indexed    => New_Vector,
                         Assign_Indexed => Assign_Element);
63/5
   function Empty_Vector (Capacity : Integer := 0) return Vector_Type;
64/5
   procedure Append_One (V : in out Vector_Type; New_Item : in String);
65/5
   procedure Assign_Element (V     : in out Vector_Type;
                             Index : in Positive;
                             Item  : in String);
66/5
   function New_Vector (First, Last : Positive) return Vector_Type
      with Pre => First = Positive'First;
      --  Vectors are always indexed starting at the
      --  lower bound of their index subtype.
67/5
-- Private part not shown.
68/5
{AI12-0212-1} {AI12-0429-1} Examples of container aggregates for Set_Type, Map_Type, and Vector_Type:
69/5
--  Example aggregates using Set_Type.
S : Set_Type;
70/5
--  Assign the empty set to S:
S := [];
71/5
--  Is equivalent to:
S := Empty_Set;
72/5
--  A positional set aggregate:
S := [1, 2];
73/5
--  Is equivalent to:
S := Empty_Set;
Include (S, 1);
Include (S, 2);
74/5
--  A set aggregate with an iterated_element_association:
S := [for Item in 1 .. 5 => Item * 2];
75/5
--  Is equivalent to:
S := Empty_Set;
for Item in 1 .. 5 loop
   Include (S, Item * 2);
end loop;
76/5
--  A set aggregate consisting of two iterated_element_associations:
S := [for Item in 1 .. 5 => Item,
      for Item in 1 .. 5 => -Item];
77/5
{AI12-0379-1} --  Is equivalent (assuming set semantics) to:
S := Empty_Set;
for Item in 1 .. 5 loop
   Include (S, Item);
end loop;
for Item in -5 .. -1 loop
   Include (S, Item);
end loop;
78/5
--  Example aggregates using Map_Type.
M : Map_Type;
79/5
--  A simple named map aggregate:
M := [12 => "house", 14 => "beige"];
80/5
--  Is equivalent to:
M := Empty_Map;
Add_To_Map (M, 12, "house");
Add_To_Map (M, 14, "beige");
81/5
--  Define a table of pairs:
type Pair is record
   Key : Integer;
   Value : access constant String;
end record;
82/5
Table : constant array(Positive range <>) of Pair :=
   [(Key => 33, Value => new String'("a nice string")),
    (Key => 44, Value => new String'("an even better string"))];
83/5
--  A map aggregate using an iterated_element_association
--  and a key_expression, built from from a table of key/value pairs:
M := [for P of Table use P.Key => P.Value.all];
84/5
--  Is equivalent to:
M := Empty_Map;
for P of Table loop
   Add_To_Map (M, P.Key, P.Value.all);
end loop;
85/5
--  Create an image table for an array of integers:
Keys : constant array(Positive range <>) of Integer := [2, 3, 5, 7, 11];
86/5
--  A map aggregate where the values produced by the
--  iterated_element_association are of the same type as the key
--  (hence a separate key_expression is unnecessary):
M := [for Key of Keys => Integer'Image (Key)];
87/5
--  Is equivalent to:
M := Empty_Map;
for Key of Keys loop
   Add_To_Map (M, Key, Integer'Image (Key));
end loop;
88/5
--  Example aggregates using Vector_Type.
V : Vector_Type;
89/5
--  A positional vector aggregate:
V := ["abc", "def"];
90/5
--  Is equivalent to:
V := Empty_Vector (2);
Append_One (V, "abc");
Append_One (V, "def");
91/5
--  An indexed vector aggregate:
V := [1 => "this", 2 => "is", 3 => "a", 4 => "test"];
92/5
--  Is equivalent to:
V := New_Vector (1, 4);
Assign_Element (V, 1, "this");
Assign_Element (V, 2, "is");
Assign_Element (V, 3, "a");
Assign_Element (V, 4, "test");

Extensions to Ada 2012

92.a/5
{AI12-0212-1} Container aggregates are new. 

Inconsistencies With Ada 2022

92.b/6
{AI22-0031-1} Corrigendum: Expressions in a named_container_aggregate are evaluated once for each key value. This is consistent with other kinds of aggregate. Original Ada 2022 evaluated the expressions once per association. If a program depends on the number of times the expressions are evaluated, the behavior could change. This sort of program is unusual, and most depend on each component having a separate evaluation, so this change is more likely to fix a bug than cause one. 

Incompatibilities With Ada 2022

92.c/6
{AI22-0002-1} Corrigendum: Unlike original Ada 2022, Empty can only be given by a function, not by a constant, as a constant does not make sense when the associated type is derived. As a new Ada 2022 feature, the specification of container aggregates should be rare, and the fix is easy: just replace the constant by an expression function returning the constant.
92.d/6
{AI22-0021-1} Corrigendum:Adjusted the rules about resolution of subprograms to avoid depending upon properties like limitedness of the parameter types (we do not do this elsewhere in the language). Also, some rules were added to make some unusual cases illegal that could have caused the aggregate implementation to be illegal. In both of these cases, unusual programs that were legal in original Ada 2022 are illegal in Ada 202y. As this is a new Ada 2022 feature, and the situations involved are unlikely, it is not expected that this will affect any existing code.
92.e/6
{AI22-0032-1} Corrigendum:The subprograms specified for an Aggregate aspect cannot be abstract unless the associated type is abstract; abstract subprograms were allowed in original Ada 2022. It is highly unlikely that any code using abstract subprograms in an Aggregate aspect exists, as such subprograms have no body but will be called when an aggregate is used for the type (usually causing internal errors in compilers).  

Extensions to Ada 2022

92.f/6
{AI22-0018-1} Corrigendum:A named_container_aggregate with a key_choice with a discrete_range no longer has to be an indexed aggregate. There was no reason for this restriction. 

Wording Changes from Ada 2022

92.g/6
{AI22-0031-1} Corrigendum: Added wording to clarify that calls defined in this subclause are evaluated as calls are defined in 6.4. While this could cause a program to raise an exception where it previously did not, this is just specifying otherwise unspecified behavior and does so as as Ada programmer would expect.
92.h/6
{AI22-0031-1} Corrigendum: Increased flexibility by removing an order requirement on the calls to Add_Indexed. This does not require any change to an existing implementation or program. 

Contents   Index   References   Search   Previous   Next 
Ada-Europe Ada 2005 and 2012 Editions sponsored in part by Ada-Europe