Ada Conformity Assessment Authority      Home Conformity Assessment   Test Suite ARGAda Standard
 
Annotated Ada Reference Manual (Ada 202x Draft 22)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.1/5
Glossary entry: A 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])
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/5
{AI12-0212-1} The name specified for Empty for an Aggregate aspect shall denote a constant of the container type, or denote a function with a result type of the container type that has no parameters, or that has one in parameter of type Integer.
6.a/5
Reason: 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/5
{AI12-0212-1} The procedure_name specified for Add_Unnamed for an Aggregate aspect shall denote a procedure that has two parameters, the first an in out parameter of the container type, and the second an in parameter of some nonlimited type, called the element type of the container type.
8/5
{AI12-0212-1} The function_name specified for New_Indexed for an Aggregate aspect shall denote a function with a result type of the container type, and two parameters of the same discrete type, 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/5
{AI12-0212-1} The procedure_name specified for Add_Named or Assign_Indexed for an Aggregate aspect shall denote a procedure that has three parameters, the first an in out parameter of the container type, the second an in parameter of a nonlimited type (the key type of the container type), and the third, an in parameter of a nonlimited type that is called the element type of the container type.

Legality Rules

10/5
{AI12-0212-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/5
{AI12-0212-1} For an Aggregate aspect, the key type of Assign_Indexed shall be the same type as that of the parameters of New_Indexed. 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.

Static Semantics

12/5
{AI12-0212-1} The Aggregate aspect is nonoverridable.

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} The expected type for a container_aggregate shall be a 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/5
{AI12-0212-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. [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/5
{AI12-0212-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/5
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/5
for a named_container_aggregate without an iterated_element_association, the number of key_expressions;
39/5
for a named_container_aggregate where every iterated_element_association 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/5
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 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, 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/5
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, 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 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 rocedure 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 in its stead; 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.

Examples

54/5
{AI12-0212-1} Declarations of Set_Type, Map_Type, and Vector_Type:
55/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;
56/5
   subtype Small_Natural is Natural range 0..1000;
57/5
   procedure Include (S : in out Set_Type; N : in Small_Natural);
58/5
   --  Map_Type is a map-like container type.
   type Map_Type is private
      with Aggregate =>  (Empty     => Empty_Map,
                          Add_Named => Add_To_Map);
59/5
   procedure Add_To_Map (M : in out Map_Type; Key : in Integer; Value : in String);
60/5
   Empty_Map : constant Map_Type;
61/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);
62/5
   function Empty_Vector (Capacity : Count_Type := 0) return Vector_Type;
63/5
   procedure Append_One (V : in out Vector_Type; New_Item : in String);
64/5
   procedure Assign_Element (V : in out Vector_Type;
                             Index : in Positive;
                             Item  : in String);
65/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.
66/5
private
67/5
   type Set_Type is new Bit_Vector (Small_Natural); -- See 3.6.
68/5
   function Empty_Set return Set_Type is (others => False);
69/5
   package Int_String_Maps is
      new Ada.Containers.Indefinite_Ordered_Maps  -- See A.18.14.
         (Key_Type => Integer, Element_Type => String);
70/5
   type Map_Type is new Int_String_Maps.Map with null record;
71/5
   procedure Add_To_Map (M : in out Map_Type; Key : in Integer; Value : in String)
      renames Insert;
72/5
   Empty_Map : constant Map_Type :=
      (Int_String_Maps.Empty_Map with null record);
73/5
   package String_Vectors is
      new Ada.Containers.Indefinite_Vectors -- See A.18.11.
         (Index_Type => Positive, Element_Type => String);
74/5
   type Vector_Type is new String_Vectors.Vector;
75/5
{AI12-0212-1} Examples of container aggregates for Set_Type, Map_Type, and Vector_Type:
76/5
--  Example aggregates using Set_Type.
S : Set_Type;
77/5
--  Assign the empty set to S:
S := [];
78/5
--  Is equivalent to:
S := Empty_Set;
79/5
--  A positional set aggregate:
S := [1, 2];
80/5
--  Is equivalent to:
S := Empty_Set;
Include (S, 1);
Include (S, 2);
81/5
--  A set aggregate with an iterated_element_association:
S := [for Item in 1 .. 5 => Item * 2];
82/5
--  Is equivalent to:
S := Empty_Set;
for Item in 1 .. 5 loop
   Include (S, Item * 2);
end loop;
83/5
--  A set aggregate consisting of two iterated_element_associations:
S := [for Item in 1 .. 5 => Item,
      for Item in 1 .. 5 => -Item];
84/5
--  Is equivalent (assuming set semantics) to:
S := Empty_Set;
for Item in -5 .. 5 loop
   if Item /= 0 then
      Include (S, Item * 2);
   end if;
end loop;
85/5
--  Example aggregates using Map_Type.
M : Map_Type;
86/5
--  Create an empty map:
M := [];
87/5
--  Is equivalent to:
M := Empty_Map;
88/5
--  A simple named map aggregate:
M := [12 => "house", 14 => "beige"];
89/5
--  Is equivalent to:
M := Empty_Map;
Add_To_Map (M, 12, "house");
Add_To_Map (M, 14, "beige");
90/5
--  Define a table of pairs:
type Pair is record
   Key : Integer;
   Value : access constant String;
end record;
91/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"))];
92/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];
93/5
--  Is equivalent to:
M := Empty_Map;
for P of Table loop
   Add_To_Map (M, P.Key, P.Value);
end loop;
94/5
--  Create an image table for an array of integers:
Keys : constant array(Positive range <>) of Integer := [2, 3, 5, 7, 11];
95/5
--  A map aggregate where the values produced by the
--  iterated_element_association are of the same type as the key
--  (eliminating the need for a separate key_expression):
M := [for Key of Keys => Integer'Image (Key)];
96/5
--  Is equivalent to:
M := Empty_Map;
for Key of Keys loop
   Add_To_Map (M, Key, Integer'Image (Key));
end loop;
97/5
--  The above could have been written using an explicit key_expression:
M := [for Key of Keys use Key => Integer'Image (Key)];
98/5
--  Example aggregates using Vector_Type.
V : Vector_Type;
99/5
--  Create an empty vector aggregate:
V := [];
100/5
--  Is equivalent to:
V := Empty_Vector (0);
101/5
--  A positional vector aggregate:
V := ["abc", "def"];
102/5
--  Is equivalent to:
V := Empty_Vector (2);
Append_One (V, "abc");
Append_One (V, "def");
103/5
--  An indexed vector aggregate:
V := [1 => "this", 2 => "is", 3 => "a", 4 => "test"];
104/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");
105/5
--  A vector of images of dynamic length:
V := [for I in 1 .. N => Integer'Image (I)];
106/5
--  Is equivalent to:
V := New_Vector (1, N);
for I in 1 .. N loop
   Assign_Element (V, I, Integer'Image (I));
end loop;
107/5
--  A vector made from the elements of a map:
V := [for Elem of M => Elem];
108/5
--  Is equivalent to:
V := Empty_Vector (<estimate of size of M>);
for Elem of M loop
   Add_Positional (V, Elem);
end loop;

Extensions to Ada 2012

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

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