4.3.5 Container Aggregates
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
{
AI12-0212-1}
For a type other than an array type, the following
type-related operational aspect may be specified:
Aggregate
This aspect is an aggregate
of the form:
Aspect Description
for Aggregate: Mechanism
to define user-defined aggregates.
(Empty => name[,
Add_Named => procedure_name][,
Add_Unnamed => procedure_name][,
New_Indexed => function_name,
Assign_Indexed => procedure_name])
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.
{
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
{
AI12-0212-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.
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.
{
AI12-0212-1}
{
AI12-0437-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 of some
nonlimited type, called the element type of the container type.
{
AI12-0212-1}
{
AI12-0437-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, with that type being the key type of
the container type.
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.
{
AI12-0212-1}
{
AI12-0437-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 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
{
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.
{
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
Syntax
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.
Name Resolution Rules
Legality Rules
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.
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.
Dynamic Semantics
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:
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;
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:
otherwise, to an
implementation-defined value.
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.
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;
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;
otherwise, with
the loop parameter as the second parameter;
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;
1.
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.
Examples
{
AI12-0212-1}
{
AI12-0429-1}
Examples of specifying the Aggregate aspect
for a Set_Type, a Map_Type, and a Vector_Type:
-- 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;
subtype Small_Int is Integer range -1000..1000;
procedure Include (S : in out Set_Type; N : in Small_Int);
-- Map_Type is a map-like container type.
type Map_Type is private
with Aggregate => (Empty => Empty_Map,
Add_Named => Add_To_Map);
procedure Add_To_Map (M : in out Map_Type;
Key : in Integer;
Value : in String);
Empty_Map : constant Map_Type;
-- 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);
function Empty_Vector (Capacity : Integer := 0) return Vector_Type;
procedure Append_One (V : in out Vector_Type; New_Item : in String);
procedure Assign_Element (V : in out Vector_Type;
Index : in Positive;
Item : in String);
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.
-- Private part not shown.
-- Example aggregates using Set_Type.
S : Set_Type;
-- Assign the empty set to S:
S := [];
-- Is equivalent to:
S := Empty_Set;
-- A positional set aggregate:
S := [1, 2];
-- Is equivalent to:
S := Empty_Set;
Include (S, 1);
Include (S, 2);
-- Is equivalent to:
S := Empty_Set;
for Item in 1 .. 5 loop
Include (S, Item * 2);
end loop;
{
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;
-- Example aggregates using Map_Type.
M : Map_Type;
-- A simple named map aggregate:
M := [12 => "house", 14 => "beige"];
-- Is equivalent to:
M := Empty_Map;
Add_To_Map (M, 12, "house");
Add_To_Map (M, 14, "beige");
-- Define a table of pairs:
type Pair is record
Key : Integer;
Value : access constant String;
end record;
Table : constant array(Positive range <>) of Pair :=
[(Key => 33, Value => new String'("a nice string")),
(Key => 44, Value => new String'("an even better string"))];
-- Is equivalent to:
M := Empty_Map;
for P of Table loop
Add_To_Map (M, P.Key, P.Value.all);
end loop;
-- Create an image table for an array of integers:
Keys : constant array(Positive range <>) of Integer := [2, 3, 5, 7, 11];
-- 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)];
-- Is equivalent to:
M := Empty_Map;
for Key of Keys loop
Add_To_Map (M, Key, Integer'Image (Key));
end loop;
-- Example aggregates using Vector_Type.
V : Vector_Type;
-- A positional vector aggregate:
V := ["abc", "def"];
-- Is equivalent to:
V := Empty_Vector (2);
Append_One (V, "abc");
Append_One (V, "def");
-- An indexed vector aggregate:
V := [1 => "this", 2 => "is", 3 => "a", 4 => "test"];
-- 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
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe