Version 1.14 of ai12s/ai12-0212-1.txt

Unformatted version of ai12s/ai12-0212-1.txt version 1.14
Other versions for file ai12s/ai12-0212-1.txt

!standard 4.3.5(0)          18-03-13 AI12-0212-1/06
!class Amendment 16-12-27
!status work item 17-06-10
!status received 16-06-12
!priority Low
!difficulty Hard
!subject Container aggregates; generalized array aggregates
!summary
Add aggregate syntax to construct container instances. Allow container iterators in array aggregates.
!problem
Currently, there are very few constructors for containers. We have for example the constant Empty_Set, and To_Set to construct a set containing the specified element. The best way to do this now is to manually assemble a new container with a series of statements; but this has a number of issues:
- cannot be used in a subprogram contract (such as a post condition) - can make code harder to read since construction has to be moved to a
separate function
An obvious approach would be to add functions that initialise a container from an aggregate of an unconstrained array, for example:
X : Example.Set := Example.From_Array ((1, 2, 3));
However there are a number of drawbacks: the syntax is ugly (double brackets), it would not allow comprehensions (i.e. containers defined by a nested iterator) (only displays -- i.e. an explicit list of components), and it would only work for array-like containers and not maps.
There are three places where container aggregates may be useful: in ordinary code (but here we can just do what we do right now), for the definition of constants, and in contracts.
!proposal
Thus, we propose to add a new aspect (in the spirit of Constant_Indexing, Default_Iterator, and Iterator_Element) along with a new kind of aggregate to use it, inspired by the array aggregate syntax.
This proposal works for most of the standard containers (Vectors, Doubly_Linked_Lists, Hashed_Sets, Ordered_Sets, Hashed_Maps and Ordered_Maps including their bounded and indefinite versions). Multiway_Trees are not supported by this proposal.
The concept is inspired in part by Python and other languages that have list and set "comprehensions," but it uses, largely, Ada's array aggregate as the basis for the syntax.
In the most simple form (called a positional container aggregate) we just give the elements to add in a form very similar to a simple array aggregate, but with no "others" permitted:
X : My_Set := (1, 2, 3);
-- Equivalent to: X : My_Set := Empty_Set; Include (X, 1); Include (X, 2); Include (X, 3);
The function or constant "Empty_Set" is one of the two (or three) things you specify in the Aggregate aspect (the "Empty" component of the Aggregate aspect), the procedure "Include" is the other (the "Add_Unnamed" component of the Aggregate aspect).
Positional container aggregates are good for defining simple container instances, but often, you might want to derive one container from another (a concept that is called comprehension). This is vaguely related to a delta aggregate, but neither construct can easily emulate the other.
Here we loop over one container and apply some function to each item. The proposed syntax steals from existing loop and quantifier syntax:
Y : My_Set := (for Item of X => Item * 2);
-- Equivalent to: Y : My_Set := Empty_Set; for Item of X loop Include (Y, Item * 2); end loop;
We may also wish to filter the items:
Z : My_Set := (for Item of X when Item > 1 => Item - 1);
-- Equivalent to: Z : My_Set := Empty_Set; for Item of X loop if Item > 1 then Include (Z, Item - 1); end if; end loop;
Finally, it is also a common pattern to combine multiple containers into one. Three approaches are considered. This is the nested (or "product") one (note that this is not part of the current proposal):
W : My_Set := (for A of X => for B of X => A * B); -- TBD: Not currently in this AI
-- Equivalent to: W : My_Set := Empty_Set; for A of X loop for B of X loop Include (W, A * B); end loop; end loop;
This is the sequential (or "list") one (this is based on the similar capability in the array aggregate to have multiple iterated component associations in a single aggregate:
V : My_Set := (for A of X => A, for A of X => -A);
-- Equivalent to: V : My_Set := Empty_Set; for A of X loop Include (V, A); end loop; for A of X loop Include (V, -A); end loop;
This is the concurrent one (note that this is not part of the current proposal, and would more properly be associated with a general enhancement to iterators -- this is not "parallel" but rather "concurrent" in that the two iterators are advanced concurrently, stopping when either one finishes):
V : My_Set := (for (A of X; I in 1..Max) => (A, I)); -- TBD: Not currently in this AI yet
-- Equivalent to: V : My_Set := Empty_Set; for (A of X; I in 1..Max) loop Include (V, (A, I)); end loop;
-- where the parenthesized iterators run concurrently, with the iteration -- stopping when either runs out of elements.
The new aspect specifications for the set container would be:
type Set is tagged private with -- currently we have this: Constant_Indexing => Constant_Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, -- this is new Aggregate => (Empty => Empty_Set, Add_Unnamed => Include);
For containers such as maps, an Add_Named component of the Aggregate aspect would be specified, to enable a usage such as:
M : My_Map := (42 => "foo", 88 => "bar");
This is equivalent to:
M := Empty_Map; Insert (M, 42, "foo"); Insert (M, 88, "bar");
where "Insert" is specified as the "Add_Named" component of the Aggregate aspect.
To accommodate array-like containers that require a contiguous range of "key" values, and might not be extensible, we allow the specification of a New_Indexed function that creates an indexed structure with the appropriate bounds, and an Assign_Indexed procedure that assigns a value into a particular indexed element. Aggregates that use these operations are called "indexed aggregates" and have many of the same rules as array aggregates with respect to uniqueness of indices and full coverage.
So, to summarise: * New aspect on types specifying an empty default, plus an
Add_Named procedure, or an Add_Unnamed procedure and/or an Assign_Indexed procedure (with a New_Indexed function to go with it).
* Extend aggregate syntax to support these based on the expected type
We also provide wording to allow container iterators within an array aggregate.
!wording
Modify 13.1.1(5/3):
An aspect_mark identifies an aspect of the entity defined by the associated declaration (the associated entity); the aspect denotes an object, a value, an expression{, an aggregate}, a subprogram, or some other kind of entity. If the aspect_mark identifies:
Add after 13.1.1(7/3):
* an aspect that is an aggregate, the aspect definition shall be an expression that is an aggregate, with the form of the aggregate determined by the identified aspect;
Replace 4.3(2/5):
aggregate ::= record_aggregate | extension_aggregate | array_aggregate | delta_aggregate
with:
aggregate ::= record_aggregate | extension_aggregate | array_aggregate | delta_aggregate | container_aggregate
Modify 4.3(3/5):
The expected type for a delta_aggregate shall be a single array type, or a single descendant of a record type or of a record extension. The expected type for any other aggregate shall be a single array type, record type, [or ]record extension{, or other composite type with the Aggregate aspect specified}.
---- wording for adding container iterators to array aggregates:
Replace 4.3.3(5.1/5):
iterated_component_association ::= for defining_identifier in discrete_choice_list => expression
with:
iterated_component_association ::= for defining_identifier in discrete_choice_list => expression | for iterator_specification => expression
Add after 4.3.3(17/5):
Either all or none of the array_component_associations of an array_component_association_list shall be iterated_component_associations with an iterator_specification.
Add 4.2.3(20) as follows:
For an array_aggregate that contains only array_component_associations that are iterated_component_associations with iterator_specifications, evaluation proceeds in two steps:
1. Each iterator_specification is elaborated (in an arbitrary order)
and an iteration is performed solely to determine a count of the number of values produced by the iteration; all of these counts are combined to determine the overall length of the array, and ultimately the bounds of the array (defined below);
2. A second iteration is performed for each of the iterator_specifications,
in the order given in the aggregate, and for each value produced, the associated expression is evaluated, its value is converted to the component subtype of the array type, and used to define the value of the next component of the array starting at the low bound and proceeding sequentially toward the high bound.
Modify 4.2.3(21) as follows:
The evaluation of [an] {any other} array_aggregate of a given array type proceeds in two steps:
Modify 4.3.3(23.2/5)
During an evaluation of the expression of an iterated_component_association {with a discrete_choice_list}, the value of the corresponding index parameter is that of the corresponding index of the corresponding array component. {During an evaluation of the expression of an iterated_component_association with an iterator_specification, the value of the loop parameter of the iterator_specification is the value produced by the iteration.}
Add after 4.3.3(26):
* For a named_array_aggregate containing only iterated_component_associations with an iterator_specification, the lower bound is determined as for a positional_array_aggregate without an OTHERS choice, and the upper bound is determined from the lower bound and the total number of values produced by the iteration(s);
---- end of wording for array aggregate enhancement ----
Add new section:
4.3.5 Container Aggregates
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.
Given a private type or private extension T, the following type-related operational aspect may be specified:
AARM Reason: We require the type to be a partial view so it is clear that the aggregate should not be interpreted as some other kind of aggregate, since syntactically it is indistinguishable.
Aggregate
This aspect is an aggregate of the form:
(Empty => name[, Add_Named => /procedure_/name][, Add_Unnamed => /procedure_/name][, New_Indexed => /function_/name, Assign_Indexed => /procedure_/name])
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
The name specified for Empty shall denote a constant of type T, or denote a function with a result type of T that has no parameters, or that has one IN parameter of type Integer with a default expression.
AARM 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.
The /procedure_/name specified for Add_Unnamed, if any, shall denote a procedure that has two parameters, the first an IN OUT parameter of type T, and the second an IN parameter of some nonlimited type, called the /element type/ of T.
The /function_/name specified for New_Indexed, if any, shall denote a function with a result type of T, and two parameters of the same discrete type, with that type being the /key type/ of the container.
AARM 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.
The /procedure_/name specified for Add_Named or Assign_Indexed, if any, shall denote a procedure that has three parameters, the first an IN OUT parameter of type T, the second an IN parameter of a nonlimited type (the /key type/ of the container), and the third, an IN parameter of a nonlimited type that is called the /element type/ of T. In the case of Assign_Indexed, the key type shall be the same type as that of the parameters of New_Indexed.
If both Add_Unnamed and Assign_Indexed are specified, the final parameters shall be of the same type -- the element type of T.
Syntax
container_aggregate ::= empty_container_aggregate | positional_container_aggregate | named_container_aggregate
empty_container_aggregate ::= (<>)
positional_container_aggregate ::= (expression, expression {, expression})
named_container_aggregate ::= (container_element_association_list)
container_element_association_list ::= container_element_association {, container_element_association}
container_element_association ::= key_choice_list => expression | key_choice_list => <> | iterated_element_association
key_choice_list ::= key_choice {| key_choice}
key_choice ::= /key_/expression | discrete_range
iterated_element_association ::= for loop_parameter_specification[, /key_/expression] => expression | for iterator_specification[, /key_/expression] => expression
Name Resolution Rules
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.
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
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. [Redundant: (An empty_container_aggregate can be of any type with an Aggregate aspect.)]
A non-empty 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.
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.
AARM 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. Note that to be consistent with array aggregates, we do not support specifying <> in an iterated_element_association.
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.
AARM 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.
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. Furthermore, for such an aggregate, either:
* 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
* there shall be exactly one container_element_association, and if it has a key_choice_list, the list shall have exactly one key_choice.
AARM Reason: The above is trying to mimic the rules for named array aggregates, without others.
Dynamic Semantics
The evaluation of a container_aggregate starts by creating an anonymous object A of the expected type T initialized as follows:
* 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 a named_container_aggregate, the lower bound is the lowest value covered by a key_choice_list or as 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.
* 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:
* for an empty_container_aggregate, the value zero;
* for a positional_container_aggregate, the number of expressions;
* for a named_container_aggregate without an iterated_element_association, the number of /key_/expressions;
* for a named_container_aggregate where every iterated_element_association is of the form with a loop_parameter_specification, the total number of elements specified by all of the container_element_associations;
* otherwise, to an implementation-defined value.
The evaluation then proceeds as follows:
* for an empty_container_aggregate, the anonymous object A is the result;
* 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;
* 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:
* 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;
* for a container_element_association with an iterated_element_association, the iterated_element_association is elaborated, and an iteration is performed, and for each value of the loop parameter of the iteration 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:
* if there is a /key_/expression, the result of evaluating the /key_/expression as the second 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 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;
* for any other named_container_aggregate, the container_element_associations (which are necessarily iterated_element_associations) are evaluated in the order given:
* the iterated_element_association is elaborated, and an iteration is performed, and for each value of the loop parameter of the iteration, 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. AARM 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
Declarations of Set_Type, Map_Type, and 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_Natural is Natural range 0..1000;
procedure Include (S : in out Set_Type; N : Small_Natural);
-- 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 : Integer; Value : 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 : Count_Type := 0) return Vector_Type;
procedure Append_One (V : in out Vector_Type; New_Item : String);
procedure Assign_Element (V : in out Vector_Type; Index : Positive; Item : String);
function New_Vector (First, Last : Positive) return Vector_Type with Pre => First = Positive'First; -- Vectors of are always indexed starting at the -- low bound of their index subtype.
private
type Set_Type is new Bit_Vector (Small_Natural); -- see 3.6
function Empty_Set return Set_Type is (others => False);
package Int_String_Maps is new Ada.Containers.Indefinite_Ordered_Maps -- see A.18.14 (Key_Type => Integer, Element_Type => String);
type Map_Type is new Int_String_Maps.Map with null record;
procedure Add_To_Map (M : in out Map_Type; Key : Integer; Value : String) renames Insert;
Empty_Map : constant Map_Type := (Int_String_Maps.Empty_Map with null record);
package String_Vectors is new Ada.Containers.Indefinite_Vectors -- see A.18. (Index_Type => Positive, Element_Type => String);
type Vector_Type is new String_Vectors.Vector;
Examples of container aggregates for Set_Type, Map_Type, and Vector_Type
-- Assign S to an empty set 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);
-- A set aggregate with an iterated_element_association S := (for Item in 1 .. 5 => Item * 2)
-- is equivalent to S := Empty_Set; for Item in 1 .. 5 loop
Include (S, Item * 2);
end loop;
-- A set aggregate with a filter (separate AI) S := (for Item in 1 .. 100 when Is_Prime (Item) => Item);
-- is equivalent to S := Empty_Set; for Item in 1 .. 5 loop if Is_Prime then Include (S, Item); end if; end loop;
-- A set aggregate consisting of two iterated_element_associations S := (for Item in 1 .. 5 => Item, for Item in 1 .. 5 => -Item);
-- Is equivalent (assuming set semantics) to S := (for Item in -5 .. 5 when Item /= 0 => Item);
-- Example aggregates using Map_Type M : Map_Type;
-- Create an empty map M := (<>);
-- is equivalent to: M := Empty_Map;
-- 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")));
-- 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, P.Key => P.Value);
-- is equivalent to: M := Empty_Map; for P of Table loop Add_To_Map (M, P.Key, P.Value); 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 -- (eliminating the need for a separate key_expression): 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;
-- The above could have been written using an explicit key_expression: M := (for Key of Keys, Key => Integer'Image (Key));
-- Example aggregates using Vector_Type V : Vector_Type;
-- Create an empty vector aggregate V := (<>);
-- Is equivalent to: V := Empty_Vector (0);
-- 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");
-- A vector of images of dynamic length V := (for I in 1 .. N => Integer'Image (I));
-- is equivalent to: V := New_Vector (1, N); for I in 1 .. N loop Assign_Element (V, I, Integer'Image (I)); end loop;
-- A vector made from the elements of a map V := (for Elem of M => Elem);
-- is equivalent to: V := Empty_Vector (<estimate of size of M>); for Elem of M loop Add_Positional (V, Elem); end loop;
A.18.2 The Generic Package Containers.Vectors
Add the Aggregate aspect to the existing ones on type Vector:
type Vector is tagged private with Constant_Indexing => Constant_Reference, Variable_Indexing => Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_Vector, Add_Unnamed => Append_One, New_Indexed => New_Vector, Assign_Indexed => Replace_Element);
-- Need to add these subprograms: function New_Vector (First, Last : Index_Type) return Vector is (To_Vector (Last)) with Pre => First = Index_Type'First;
procedure Append_One (Container : in out Vector; New_Item : in Element_Type);
A.18.3 The Generic Package Containers.Doubly_Linked_Lists
Add the Aggregate aspect to the existing ones on type List:
type List is tagged private with Constant_Indexing => Constant_Reference, Variable_Indexing => Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_List, Add_Unnamed => Append);
A.18.5 The Generic Package Containers.Hashed_Maps
Add the Aggregate aspect to the existing ones on type Map:
type Map is tagged private with Constant_Indexing => Constant_Reference, Variable_Indexing => Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_Map, Add_Named => Insert);
A.18.6 The Generic Package Containers.Ordered_Maps
Add the Aggregate aspect to the existing ones on type Map:
type Map is tagged private with Constant_Indexing => Constant_Reference, Variable_Indexing => Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_Map, Add_Named => Insert);
A.18.8 The Generic Package Containers.Hashed_Sets
Add the Aggregate aspect to the existing ones on type Set:
type Set is tagged private with Constant_Indexing => Constant_Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_Set, Add_Unnamed => Include);
A.18.9 The Generic Package Containers.Ordered_Sets
Add the Aggregate aspect to the existing ones on type Set:
type Set is tagged private with Constant_Indexing => Constant_Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_Set, Add_Unnamed => Include);
!discussion
See !proposal for most of the discussion. Here we discuss some rejected alternatives or additions.
We originally used "Add_Positional" instead of "Add_Unnamed" in the Aggregate aspect, but that created confusion because the Add_Unnamed operation is used for both named and positional aggregates. Add_Unnamed is used for named aggregates when they use a container iterator without a specified key-expression.
Before we had Assign_Indexed, we allowed both Add_Unnamed and Add_Named for a single type. But in fact, it really doesn't make much sense to allow both, since what would be the "default" value of the key if you were to apply Add_Unnamed to something like a "map" container. On the other hand, both Add_Unnamed and Assign_Indexed can be specified for the same (vector-like) type, since the container maintains a high bound for its index type, so Add_Unnamed can by default extend the vector and assign the new last element.
We include above a generalization of array aggregates to allow container iterators. Originally we were concerned about making this generalization, because to determine the size of the array to create, you will need to execute the iterator until it ends. We initially presumed you would have to save all of the values somewhere, create the array, and finally initialize the array components. However, by just getting cursors but not the actual iterator elements, you can do the iteration efficiently and determine an upper bound for the size of the array.
We use a simple "," to separate the iterator from the key => value when there is an explicit key_expression:
As illustrated in an example above, we write:
M := (for P of Table, P.Key => P.Value); -- yes
We considered using two "=>" but we believe it made it harder to read:
M := (for P of Table => P.Key => P.Value); -- no, harder to read
The comma seems to read quite nicely, without any need for extra parentheses, or multiple "=>"s.
!ASIS
!ACATS test
ACATS B and C-Tests are needed to check that the new capabilities are supported.
!appendix

[Editor's note: This idea originated during ARG meeting #55 in Pisa,
during the discussion of AI12-0189-1,
see http://www.ada-auth.org/ai-files/minutes/min-1606.html#AI189]

****************************************************************

From: Tucker Taft
Sent: Tuesday, October 11, 2016  12:04 PM

Hi Randy and Jeff,
    Thanks both of you for your great job organizing and running the ARG in
Pittsburgh.

We talked about container aggregates several times.  I see it is on Steve's
plate officially.  Did such an AI ever get created?  Raphael has expressed
an interest in helping on this one...

****************************************************************

From: Steve Baird
Sent: Wednesday, October 12, 2016  12:30 PM

Container aggregates are on Florian's plate with a promise of assistance from
me (like what we did with delta aggregates).

See Pisa minutes.

****************************************************************

From: Raphael Amiard
Sent: Tuesday, October 11, 2016  12:04 PM

Florian depending on the quantity of homework you have I'd be happy to help
out or even take over this one. Just tell me :)

****************************************************************

From: Florian Schanda
Sent: Monday, October 17, 2016  7:02 AM

I have it indeed in my personal notes as an action on me (and I will enlist
the help of Steve et al).

****************************************************************

From: Florian Schanda
Sent: Saturday, June 10, 2017  3:58 AM

Steve,

I know it said "with help from Steve" but I didn't really ask you for anything
since I was somewhat disorganized this interval. Sorry! However, you will
notice that the proposal includes a number of cries for help now, but I
figured I should post something vaguely substantial so we have a starting
point for discussion before we start sorting through the details.

Randy,

Sorry for the lateness, I've not been as active as I had anticipated this
interval. But I should have more time next interval :/

AI glued below. [Editor's note: this was version /01 of the AI.]
Enjoy, and see you all in Vienna!

****************************************************************

From: Randy Brukardt
Sent: Monday, June 12, 2017  6:02 PM

A couple of barely-informed thoughts on this one, too. (I read each of these as
I file it, thus some quick thoughts on each.)

...
> Enjoy, and see you all in Vienna!

Yes, and we can discuss the details some more there.

First of all, the basic idea seems sound. Having a couple of aspects to control
how such aggregates get constructed seems pretty sensible.

...

> Instead of "if" we could use "|" to mimic common mathematical
> notation, but it is probaby not a good idea to introduce the vertical
> bar into Ada at this point?

The vertical bar has been used in Ada to compose choice lists from the beginning
of time. It would be awful to try to use it for something else in the same
construct (that is, an aggregate). Since a "for" is just a kind of choice
(recall that we already added that in Ada 202x, see AI12-0061-1), which can be
used along with other forms of choices, using "|" in the syntax of some other
kind of choice would be confusing.

...
> Related changes:
>
> This AI will have some interesting interplay with let expressions and
> generators.
>
> This AI will (trivially) conflict with AI12-0127-1 (partial
> aggregates) as both add a new section to 4.3 and modify the syntax for
> 4.3.

It clearly interacts with AI12-0061-1 somewhat, as that also defines a "for"
syntax for aggregates. Probably no actual overlapping rules, but the two
syntaxes need to "feel" the same.

...
> The evaluation of a display_aggregate or comprehension_aggregate
> starts by creating a new object, evaluating the Empty function of the
> Comprehension aspect and assigning its result to the new object.
>
> For a unary_display the unary inclusion procedure is called for each
> expression in order, for a binary_display the binary inclusion
> procedure is called for each pair described by the binary_association.
...

I don't much like the name "unary_display": I'm reading a "display", my compiler
uses "displays" to implement up-level access to local variables, and I really
don't see any point in introducing yet another use of the term. We're talking
about container aggregates, why not call them that??

Re: "in order". Components in an Ada aggregate generally are evaluated in an
arbitrary order. I think we need to have a really good reason to go away from
that. So either you need to explain why the unusual rules or adjust this to be
in an arbitrary order. (Note that the new "for" construct executes in an
arbitrary order.)

****************************************************************

From: Florian Schanda
Sent: Tuesday, June 13, 2017  1:43 AM

> I don't much like the name "unary_display": I'm reading a "display",
> my compiler uses "displays" to implement up-level access to local
> variables, and I really don't see any point in introducing yet another use
> of the term.
> We're talking about container aggregates, why not call them that??

I think calling display container aggregate would be fine. But we should stick
with the term "comprehension" for the, well, comprehensions.

> Re: "in order". Components in an Ada aggregate generally are evaluated
> in an arbitrary order. I think we need to have a really good reason to
> go away from that. So either you need to explain why the unusual rules
> or adjust this to be in an arbitrary order. (Note that the new "for"
> construct executes in an arbitrary order.)

We soon have precedence for this: the delta aggregates are also evaluated LTR.
Plus, if you think about it; an aggregate for a list where the elements are
added in arbitrary order would be a bit counter-intuitive.

Or maybe I am missing a finer point here, and you mean we evaluate in
arbitrary order, but still add LTR?

****************************************************************

From: Randy Brukardt
Sent: Tuesday, June 13, 2017  1:06 PM

> > I don't much like the name "unary_display": I'm reading a "display",
> > my compiler uses "displays" to implement up-level access to local
> > variables, and I really don't see any point in introducing
> yet another use of the term.
> > We're talking about container aggregates, why not call them that??
>
> I think calling display container aggregate would be fine.
> But we should stick with the term "comprehension" for the, well,
> comprehensions.

WTF is a "comprehension"? I'd rather not stuff more obscure terminology into
Ada that is mostly going to get in the way of getting the job done.

> > Re: "in order". Components in an Ada aggregate generally are
> > evaluated in an arbitrary order. I think we need to have a really
> > good reason to go away from that. So either you need to explain why
> > the unusual rules or adjust this to be in an arbitrary order. (Note that
> > the new "for" construct executes in an arbitrary order.)
>
> We soon have precedence for this: the delta aggregates are also
> evaluated LTR.
> Plus, if you think about it; an aggregate for a list where the
> elements are added in arbitrary order would be a bit
> counter-intuitive.
>
> Or maybe I am missing a finer point here, and you mean we evaluate in
> arbitrary order, but still add LTR?

Well, I was actually thinking about maps, in which the order of insertion
should be irrelevant.

But you're right in that a list or vector aggregate is closest to an array
aggregate, and for that the order matters (if it is positional). But even
there, the order of evaluation is unspecified.

For maps/sets, the order of insertiong should be irrelevant. Same with a
named vector (do you have that? You ought to).

I didn't see what you proposed for a map, but IMHO I think it ought to look
something like:

   ("Tuck" => US, "Jeff" => UK, "Tuillo" => Italy, ...)

One easily can imagine a vector that works similarly, using the indexes:

   (1 => "Tuck", 2 => "Jeff", 3 => "Randy", 4 => "Florian", ...)

After all, Ada programmers learn that positional anything is bad at an early
stage; we don't want to force the use of positional aggregates unless there
is no other choice. (Yes, for a list, it seems that there is no sane way to
name the nodes, so positional is the only possibility.)

****************************************************************

From: Florian Schanda
Sent: Wednesday, June 14, 2017  2:29 AM

> WTF is a "comprehension"? I'd rather not stuff more obscure
> terminology into Ada that is mostly going to get in the way of getting the
> job done.

It's a reasonable common construct in functional programming, but also
features in some imperatives languages. Here is an overview from wikipedia:

https://en.wikipedia.org/wiki/Comparison_of_programming_languages_%28list_comprehension%29

But I think the language that made the term popular was Python:

https://docs.python.org/2/reference/expressions.html#list-displays

They also distinguish between displays and comprehension; in Python a list can
be written as [1, 2, 3] which most people think of as a literal, but it is
actually a display.

Again, I hesitate to mention Python as my main inspiration for this because,
well, Python has many flaws, but I think this is one of the more elegant parts
of the language and I think our proposed approach to make this syntax work for
user-defined types and not just built-in types really sets it apart from other
languages.

> For maps/sets, the order of insertiong should be irrelevant. Same with
> a named vector (do you have that? You ought to).

Yes; all that matters is which "inclusion" function you specify (I've done it
for all the standard containers), for sets it is include (so (1, 2, 2, 3)
will do the obvious), for maps it is insert (since 1 => 2, 1 => 3 makes little
sense) and for lists it is append.

> I didn't see what you proposed for a map, but IMHO I think it ought to
> look something like:
>
>    ("Tuck" => US, "Jeff" => UK, "Tuillo" => Italy, ...)

Yes, that is what I have. I've called these binary because the inclusion
function has two parameters (in addition to the container) instead of just one.

> One easily can imagine a vector that works similarly, using the indexes:
>
>    (1 => "Tuck", 2 => "Jeff", 3 => "Randy", 4 => "Florian", ...)

We could do that, but it seems painful, so I'd rather not come up with grammar
that allows it. It would also mean the compiler would have to sort the list
first and that also seems like unnecessary complexity ;)

****************************************************************

From: Tucker Taft
Sent: Wednesday, June 14, 2017  3:42 AM

> WTF is a "comprehension"? I'd rather not stuff more obscure terminology into
> Ada that is mostly going to get in the way of getting the job done.

A “set comprehension” is a well defined term in set theory.  E.g.:

   https://en.wikipedia.org/wiki/Set-builder_notation

Other sorts of "comprehensions" are generalizations of this notion.  But I
agree we don’t need to introduce this term into Ada.  We have already added
something that is essentially an "array comprehension" and we called it an
"iterated_component_association."  I don’t love that term either, but we
should probably stick with it.  I also find “display” a particularly
ambiguous term, and see no reason to use it here.

For what it is worth, I will send as a separate e-mail the ParaSail
description of user-defined container aggregates (as a PDF).  Not sure what
Randy’s e-mail system is going to do with that.

****************************************************************

From: Tucker Taft
Sent: Wednesday, June 14, 2017  3:45 AM

Here it is.  Actually, this is for “Sparkel,” the SPARK-like variant of
araSail.

[Sorry, there is no way to attach that here. - Editor.]

****************************************************************

From: Jeff Cousins
Sent: Wednesday, June 14, 2017  4:00 AM

> WTF is a "comprehension"?

Glad you said that before I did!

****************************************************************

From: Randy Brukardt
Sent: Wednesday, June 14, 2017  2:26 PM

>They also distinguish between displays and comprehension; in Python a list can
>be written as [1, 2, 3] which most people think of as a literal, but it is
>actually a display.

I don't see the point in differentiation: in Ada, an aggregate is often though
of as a literal, but it can contain many dynamically determined parts, so there
really is no analogue in other languages.

And it is really is a small step from:

    (For I in Func1 .. Func2 => Func3(I))

which is just an Ada 202x array aggregate, and your proposed:

    (For E of Cont1 => Func(E))

No reason that I can see to differentiate them; your "displays" and
"comphrehensions" would have the exact same implementation outside of the
looping mechanism (and us implementers are used to that vis-a-vis aggregates).

>> One easily can imagine a vector that works similarly, using the indexes:
>>
>>    (1 => "Tuck", 2 => "Jeff", 3 => "Randy", 4 => "Florian", ...)
>
>We could do that, but it seems painful, so I'd rather not come up with grammar
>that allows it. It would also mean the compiler would have to sort the list
>first and that also seems like unnecessary complexity ;)

???

The grammar is that of an array aggregate, supplemented with your "for E of
whatever" syntax. No reason to do anything else. The semantics are different,
of course, but as little as necessary.

There's no requirement to "sort" the aggregate; the implementation would use
Insert to implement them, in any order it wants. It shouldn't matter (I guess
it would have to be a new Insert analog that expands the capacity as needed)
what order is used, as we're inserting by index. The implementation *could*
sort the aggregate so the capacity is allocated all at once, but it doesn't
have to do that optimization. Indeed, your original proposal would *require*
worst-case insertion behavior (where the capacity is increased multiple times,
the minimum amount each time, until it is big enough - yuck).

Anyway, to be discussed.

****************************************************************

From: Tucker Taft
Sent: Thursday, June 22, 2017  11:19 AM

> >They also distinguish between displays and comprehension; in Python a
> >list can be written as [1, 2, 3] which most people think of as a
> >literal, but it is actually a display.
>
> I don't see the point in differentiation: in Ada, an aggregate is often
> though of as a literal, but it can contain many dynamically determined
> parts, so there really is no analogue in other languages.
>
> And it is really is a small step from:
>     (For I in Func1 .. Func2 => Func3(I)) which is just an Ada 202x
> array aggregate, and your proposed:
>     (For E of Cont1 => Func(E))
> No reason that I can see to differentiate them; your "displays" and
> "comphrehensions" would have the exact same implementation outside of the
> looping mechanism (and us implementers are used to that vis-a-vis aggregates). ...

I agree, we should use named and positional array aggregate syntax (and
vocabulary) for these new container aggregates, unless there is a compelling
reason to invent something new.

****************************************************************

From: Tucker Taft
Sent: Friday, January 19, 2018  1:28 PM

I offered to take this one over from Florian, because he seems to be swamped
with other work, and I think this one is very important.  Essentially a
complete re-write, using terms "positional" and "named" container aggregates.
Also, introducing terms "element type" and "key type."  Hopefully it is
understandable.

[This is version /02 of the AI - ED.]
Comments welcome!

****************************************************************

From: Edmond Schonberg
Sent: Friday, January 19, 2018   1:48 PM

Looks great, a much-needed addition to the language!

****************************************************************

From: Randy Brukardt
Sent: Wednesday, January 24, 2018  11:29 PM

> Essentially a complete re-write, using terms "positional" and "named"
> container aggregates.

A few comments:

Are you using some funny editor/machine to create these attachments? They
look normal when opened in Notepad, but when I try to cut-and-paste pieces
(say into an e-mail message), the text comes out all on one line jammed
together. Linux files don't even work in Notepad (are just one long line),
so with those I know they need correction right away.

At least reading them with an Ada program and writing them out again
(Get_Line/Put_Line) makes a usable version.

---

There's some references in the proposal section to things that aren't going to
be in *this* AI ("concurrent aggregates"), and "related changes" seems
speculative at best. I'd just delete all of that.

---

> Given a private type or private extension T, the following
> type-related operational aspect may be specified:
>
>  Aggregate
>    This aspect is of the form:
>
>      (Empty => name[,
>       Add_Positional => /procedure_/name][,
>       Add_Named => /procedure_/name])
>
>    Either Add_Positional, Add_Named, or both shall be specified.

It took me a long time to figure out that there isn't a need for special syntax
here. I eventually realized that you are using a clever: this has the syntax of
an aggregate, so this aspect is being specified by an expression.

But this is a massive hack (and doesn't fit the rules given in 13.1.1,
specifically 13.1.1(7/3) and 13.1.1(21/3)): this "expression" has no definable
type, could never resolve by conventional means, can't be "evaluated to a
single value", doesn't match freezing rules, and probably more. I suppose your
intent is that this would have to be implemented with aspect-specific code
anyway, so it isn't a hardship to take an uninterpreted tree (required by
aspects anyway) and apply a custom interpretation to it.

However, if we're going to use a clever hack here, we have to define it properly
(with rule changes in 13.1.1). Also, we ought to do the same for other aspects.
In particular, the Stable_Properties aspects (for which I defined new syntax to
support lists of names, and which we already approved) probably should be
defined as a typeless positional array aggregate. Also, the Default_Iterator
and Iterator_Element probably should have a combined version (easier to use and
test).

Alternatively, we should simply (?) define a set of three interrelated aspects,
as we did for those other existing aspects. I suspect the amount of work for
this expression hack would be as much or more as defined three aspects. (At a
minimum, one would need several pages of code just to check that the arbitrary
expression that you are given actually is an aggregate, with 2 or 3 choices,
that each of those choices is a name, that each of those choices is in named
notation, that the names are the right ones... That would be extra code, not
needed for separate aspects, probably much bigger than the checks that
the Empty_Aggregate aspect was defined for the others.)

---

>If both Add_Positional and Add_Named are specified, the final
>parameters shall be of the same type -- the element type of T.

This rule is given under Name Resolution Rules, but this is clearly a Legality
Rule -- we never want to make resolution too smart. It should be moved.

---

> 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.

I don't think this works. All type aspects are independent of view
(see 13.1.1(27/3)). As such, it also applies to the full view, and as such the
common case of a full record type would define both record and container
aggregates.

I suppose we could use the "unless otherwise specified" exception, but I don't
want to start making exceptions to type aspects, that opens a Pandora's box of
anomalies. Besides, we don't (well, at least *I* don't) want container
aggregates to disappear for full types that don't have aggregates of their
own. It's just the conflict cases that need solution.

[Aside: I think this is probably the right solution for prefixed names as
well; allow the prefixed names to be used on operations that can see the full
type, but only if they are defined for the private type, and only with the
semantics of the private type -- thus a private type completed with an access
type would allow prefixed calls, but a dereference of the access type would
not be considered as a possible prefix. A bit wonky, perhaps, but worlds
better than prefixed notation disappearing when a declaration is moved a few
lines after the full type.]

Anyway, I suggest that any type with a conflict be resolved in terms of using
the built-in aggregates rather than the aspect. Perhaps:

    The expected type for a container_aggregate shall be a type for which
    the Aggregate aspect has been specified and which is not an array or record
    type.

---

>  * for a named_container_aggregate for a type with an Add_Named procedure
>    in its Aggregate aspect, the container_element_associations are evaluated
>    in any order:

I think the wording is "an unspecified order". It's this wording that triggers
the anti-order-dependence checks of 6.4.1. There are several more of these.

---

>Examples
>
>I KNOW I NEED TO REWORK THESE TO USE EXISTING THINGS IN THE RM

That's been in the AI for a while now, when is this happening?

---

>   --  Is (mostly, assuming set semantics) equivalent to
>   S := (for Item in -5 .. 5 when Item /= 0 => Item);

There are several examples using "when" in here; those should be removed from
this AI which doesn't include "when" in the syntax.

---

There aren't any examples of named aggregates, I was trying to figure out the
usage of the key_expression in an iterator and didn't find any.

---

>A.18.2 The Generic Package Containers.Vectors
>
>Add the Aggregate aspect to the existing ones on type Vector:
>
>   type Vector is tagged private
>      with Constant_Indexing => Constant_Reference,
>           Variable_Indexing => Reference,
>           Default_Iterator  => Iterate,
>           Iterator_Element  => Element_Type,
>           Aggregate         => (Empty          => Empty_Vector,
>                                 Add_Positional => Append);

It might not be worth the effort, but it seems annoying that one cannot write
named array aggregates for Vectors. After all, the idea is to make these as
close to arrays as we can. It is especially annoying that one can't write an
iterator for these:

      (for I in 1..10 => I)

to initialize a Vector with the values 1 through 10. (Since we just finally
fixed this problem for regular array aggregates.)

One possibility would be to define an Add_Named for this purpose, although
we'd need a new length-expanding Insert operation for the purpose (since the
order isn't defined). With that change, named aggregates could be written, but
without ranges (other than in iterators), and without a check for missing
elements (which seems like a bridge too far for this aspect in any case).

---

And that's all I've got. The basic ideas seem sound. And it is much more
pleasant that the original version, having lost all of the terminology and
feature gee-gaws that were jammed into the original proposal (and of which
some vestiges remain).

****************************************************************

From: Tucker Taft
Sent: Thursday, January 25, 2018  3:51 AM

>> Essentially a complete re-write, using terms "positional" and "named"
>> container aggregates.
>
> A few comments:
>
> Are you using some funny editor/machine to create these attachments?
> They look normal when opened in Notepad, but when I try to
> cut-and-paste pieces (say into an e-mail message), the text comes out
> all on one line jammed together. Linux files don't even work in
> Notepad (are just one long line), so with those I know they need correction right away.
>
> At least reading them with an Ada program and writing them out again
> (Get_Line/Put_Line) makes a usable version.

Nothing special in the way of an editing tool (TextWrangler typically, or
vi/vim), though I might have neglected to add newlines in this one.  Usually I
try to keep the lines short, but the editor automatically wraps when displaying,
so sometimes I leave in some very long lines.

> ---
>
> There's some references in the proposal section to things that aren't
> going to be in *this* AI ("concurrent aggregates"), and "related
> changes" seems speculative at best. I'd just delete all of that.

I didn't want to delete any of Florian's original examples.  Feel free.

> ---
>
>> Given a private type or private extension T, the following
>> type-related operational aspect may be specified:
>>
>> Aggregate
>>   This aspect is of the form:
>>
>>     (Empty => name[,
>>      Add_Positional => /procedure_/name][,
>>      Add_Named => /procedure_/name])
>>
>>   Either Add_Positional, Add_Named, or both shall be specified.
>
> It took me a long time to figure out that there isn't a need for
> special syntax here. I eventually realized that you are using a
> clever: this has the syntax of an aggregate, so this aspect is being specified
> by an expression.

I agree we probably need more syntax here.  The Global annotation uses similar
kinds of aggregate-like syntax, and I think it is better to keep these things
together in a single "aggregate-like" syntax.

> But this is a massive hack (and doesn't fit the rules given in 13.1.1,
> specifically 13.1.1(7/3) and 13.1.1(21/3)): this "expression" has no
> definable type, could never resolve by conventional means, can't be
> "evaluated to a single value", doesn't match freezing rules, and
> probably more. I suppose your intent is that this would have to be
> implemented with aspect-specific code anyway, so it isn't a hardship
> to take an uninterpreted tree (required by aspects anyway) and apply a
> custom interpretation to it.
>
> However, if we're going to use a clever hack here, we have to define
> it properly (with rule changes in 13.1.1). Also, we ought to do the
> same for other aspects. In particular, the Stable_Properties aspects
> (for which I defined new syntax to support lists of names, and which
> we already approved) probably should be defined as a typeless
> positional array aggregate. Also, the Default_Iterator and
> Iterator_Element probably should have a combined version (easier to use and
> test).
>
> Alternatively, we should simply (?) define a set of three interrelated
> aspects, as we did for those other existing aspects.

I don't like having a set of separate aspects that are implicitly tied together
like that.  Convention, Import, and Export were probably a mistake.

> I suspect the amount of
> work for this expression hack would be as much or more as defined
> three aspects. (At a minimum, one would need several pages of code
> just to check that the arbitrary expression that you are given
> actually is an aggregate, with 2 or 3 choices, that each of those
> choices is a name, that each of those choices is in named notation, that
> the names are the right ones...
> That would be extra code, not needed for separate aspects, probably
> much bigger than the checks that Empty_Aggregate aspect was defined
> for the
> others.)

I do not think we should be worried about the parsing expense here.  Almost
every complex pragma has special parsing rules, and we seem to have survived
that.

> ---
>
>> If both Add_Positional and Add_Named are specified, the final
>> parameters shall be of the same type -- the element type of T.
>
> This rule is given under Name Resolution Rules, but this is clearly a
> Legality Rule -- we never want to make resolution too smart.

Yes, I suppose this rule shouldn't be used to help resolve the procedures --
that would be a whole new kind of overload resolution.

> ---
>
>> 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.
>
> ...

> Anyway, I suggest that any type with a conflict be resolved in terms
> of using the built-in aggregates rather than the aspect.
> Perhaps:
>
>    The expected type for a container_aggregate shall be a type for which
>    the Aggregate aspect has been specified and which is not an array
> or record type.

I think we should be more explicit here if we want to try to make this
distinction, and indicate that the expected type cannot be the full view of an
array or record type, but it can be the partial view of such a type.

> ---
>
>> * for a named_container_aggregate for a type with an Add_Named procedure
>>   in its Aggregate aspect, the container_element_associations are evaluated
>>   in any order:
>
> I think the wording is "an unspecified order". It's this wording that
> triggers the anti-order-dependence checks of 6.4.1. There are several
> more of these.

Actually, I believe the more frequent phraseology is "in an arbitrary order"
(e.g. see 5.2(7), 6.4(10), 6.4.1(17), etc.).

> ---
>
>> Examples
>>
>> I KNOW I NEED TO REWORK THESE TO USE EXISTING THINGS IN THE RM
>
> That's been in the AI for a while now, when is this happening?

I don't think reviewing examples at that level of detail is the kind of thing we
are going to be doing in a phone call.  Remember this was a complete re-write.
My priorities were to focus on the part that might be reviewed in the 29-Jan
call.

 ---
>
>>  --  Is (mostly, assuming set semantics) equivalent to  S := (for
>> Item in -5 .. 5 when Item /= 0 => Item);
>
> There are several examples using "when" in here; those should be
> removed from this AI which doesn't include "when" in the syntax.

I had intended those to have an appropriate comment that the "when" syntax would
be discussed in a separate AI.

> ---
>
> There aren't any examples of named aggregates, I was trying to figure
> out the usage of the key_expression in an iterator and didn't find any.

Good point.  Here is a simple one where you have a table of keys and values,
and you want to create a mapping built from that table:

   Map : constant Mapping := (for I in Table'Range, Table(I).Key => Table(I).Value);


>
> ---
>
>> A.18.2 The Generic Package Containers.Vectors
>>
>> Add the Aggregate aspect to the existing ones on type Vector:
>>
>>  type Vector is tagged private
>>     with Constant_Indexing => Constant_Reference,
>>          Variable_Indexing => Reference,
>>          Default_Iterator  => Iterate,
>>          Iterator_Element  => Element_Type,
>>          Aggregate         => (Empty          => Empty_Vector,
>>                                Add_Positional => Append);
>
> It might not be worth the effort, but it seems annoying that one
> cannot write named array aggregates for Vectors. After all, the idea
> is to make these as close to arrays as we can. It is especially
> annoying that one can't write an iterator for these:
>
>      (for I in 1..10 => I)
>
> to initialize a Vector with the values 1 through 10. (Since we just
> finally fixed this problem for regular array aggregates.)

This is allowed.  You can use named notation even if there is no Add_Named.  An
iterated component association just turns into a sequence of calls on
Add_Positional.  I think that is pretty important.

> One possibility would be to define an Add_Named for this purpose,
> although we'd need a new length-expanding Insert operation for the
> purpose (since the order isn't defined). With that change, named
> aggregates could be written, but without ranges (other than in
> iterators), and without a check for missing elements (which seems like
> a bridge too far for this aspect in any case).

I thought about that, but it seems kludgey.  I prefer just allowing the use of
Add_Positional as illustrated above.  Perhaps the wording needs to be clarified
that, in the absence of Add_Named, you can still use iterated component
associations, so long as there are no key_expressions.  Here are some extracts
from the AI that permit the use of Add_Positional for a named array aggregate,
so long as it has no key_expressions:

   "... The expected type for a named_container_aggregate that has one or more
     /key_/expressions shall have an Aggregate aspect that includes a
     specification for the Add_Named procedure. ..."

  " ...  for a named_container_aggregate for a type without an Add_Named
     procedure in its Aggregate aspect, the container_element_associations
     (which are necessarily iterated_element_associations)
     are evaluated in the order given: ..."

Perhaps the "intro" paragraph is the place to mention this possibility more
explicitly.

> ---
>
> And that's all I've got. The basic ideas seem sound. And it is much
> more pleasant that the original version, having lost all of the
> terminology and feature gee-gaws that were jammed into the original
> proposal (and of which some vestiges remain).

OK, glad to hear that it looks better to you.

****************************************************************

From: Randy Brukardt
Sent: Thursday, January 25, 2018  5:08 PM

> > At least reading them with an Ada program and writing them out again
> > (Get_Line/Put_Line) makes a usable version.
>
> Nothing special in the way of an editing tool (TextWrangler typically,
> or vi/vim), though I might have neglected to add newlines in this one.
> Usually I try to keep the lines short, but the editor automatically
> wraps when displaying, so sometimes I leave in some very long lines.

Yes, that's a problem, too, but I'm used to that.

Opening one of these files in an ancient MS-DOS editor shows lots of little
musical note characters sprinkled about. I think those are unpaired <CR>s (or
unpaired <LF>s? I can't find a character chart). I suppose I could trying
looking in a hex editor...

> >
> > ---
> >
> > There's some references in the proposal section to things that
> > aren't going to be in *this* AI ("concurrent aggregates"), and
> > "related changes" seems speculative at best. I'd just delete all of that.
>
> I didn't want to delete any of Florian's original examples.
> Feel free.

It's in the !proposal, not the !problem. Talking about proposals not made is
just confusing.

> >> Given a private type or private extension T, the following
> >> type-related operational aspect may be specified:
> >>
> >> Aggregate
> >>   This aspect is of the form:
> >>
> >>     (Empty => name[,
> >>      Add_Positional => /procedure_/name][,
> >>      Add_Named => /procedure_/name])
> >>
> >>   Either Add_Positional, Add_Named, or both shall be specified.
> >
> > It took me a long time to figure out that there isn't a need for
> > special syntax here. I eventually realized that you are using a
> > clever: this has the syntax of an aggregate, so this aspect
> is being specified by an expression.
>
> I agree we probably need more syntax here.  The Global annotation uses
> similar kinds of aggregate-like syntax, and I think it is better to
> keep these things together in a single "aggregate-like" syntax.

OK. It may make sense to try to define that separately (so that multiple aspects
can use it). But in any case, it shouldn't appear here without either a
definition or a cross-reference to a different AI.

...
> > Alternatively, we should simply (?) define a set of three
> > interrelated aspects, as we did for those other existing aspects.
>
> I don't like having a set of separate aspects that are implicitly tied
> together like that.  Convention, Import, and Export were probably a
> mistake.

Didn't think of those. These are more like Default_Iterator and Iterator_Type
(one has to be specified before the other, as I recall). That says that there
are at least three sets like that.

> > I suspect the amount of
> > work for this expression hack would be as much or more as defined
> > three aspects. (At a minimum, one would need several pages of code
> > just to check that the arbitrary expression that you are given
> > actually is an aggregate, with 2 or 3 choices, that each of those
> > choices is a name, that each of those choices is in named notation,
> > that the names are the right ones...
> > That would be extra code, not needed for separate aspects, probably
> > much bigger than the checks that Empty_Aggregate aspect was defined
> > for the
> > others.)
>
> I do not think we should be worried about the parsing expense here.
> Almost every complex pragma has special parsing rules, and we seem to
> have survived that.

I wasn't thinking about "parsing expense", I was thinking about "legality check
expense". If one uses the syntax of an expression, then it will be parsed as an
expression, and then that has to be checked piece-by-piece for correctness.

And, as always, two wrongs don't make a right. Some of the pragmas are hard to
get right and take a lot of one-time use code (so it doesn't get tested much.
This is one of the reasons I am so happy to get rid of pragmas!

...
> > ---
> >
> >> 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.
> >
> > ...
>
> > Anyway, I suggest that any type with a conflict be resolved
> in terms
> > of using the built-in aggregates rather than the aspect.
> > Perhaps:
> >
> >    The expected type for a container_aggregate shall be a type for which
> >    the Aggregate aspect has been specified and which is not an array
> >    or record type.
>
> I think we should be more explicit here if we want to try to make this
> distinction, and indicate that the expected type cannot be the full
> view of an array or record type, but it can be the partial view of
> such a type.

Fair enough. I'm sure you'll propose some wording.

> > ---
> >
> >> * for a named_container_aggregate for a type with an Add_Named procedure
> >>   in its Aggregate aspect, the container_element_associations are evaluated
> >>   in any order:
> >
> > I think the wording is "an unspecified order". It's this wording
> > that triggers the anti-order-dependence checks of 6.4.1. There are
> > several more of these.
>
> Actually, I believe the more frequent phraseology is "in an arbitrary
> order" (e.g. see 5.2(7), 6.4(10), 6.4.1(17), etc.).

You're right. In any case, "any order" is not the proper wording. (I suppose you
inherited that.)

> > ---
> >
> >> Examples
> >>
> >> I KNOW I NEED TO REWORK THESE TO USE EXISTING THINGS IN THE RM
> >
> > That's been in the AI for a while now, when is this happening?
>
> I don't think reviewing examples at that level of detail is the kind
> of thing we are going to be doing in a phone call.
> Remember this was a complete re-write.  My priorities were to focus on
> the part that might be reviewed in the 29-Jan call.

Well, these calls are normal ARG meetings, so anything that we would do in a
normal meeting is fair game. In particular, we can approve AIs -- but we can't
do that until the examples are right.

I agree this probably isn't ready for approval yet, but it might be next time,
and this note was really about stuff to put into your next rewrite. (I'm only
asking for one rewrite per meeting; more are OK, but I don't want to be
unreasonable.)

> >
> > ---
> >
> >>  --  Is (mostly, assuming set semantics) equivalent to  S := (for
> >> Item in -5 .. 5 when Item /= 0 => Item);
> >
> > There are several examples using "when" in here; those should be
> > removed from this AI which doesn't include "when" in the syntax.
>
> I had intended those to have an appropriate comment that the "when"
> syntax would be discussed in a separate AI.

I suppose that would work, assuming of course that that AI goes anywhere.

> > ---
> >
> > There aren't any examples of named aggregates, I was trying to
> > figure out the usage of the key_expression in an iterator and didn't find
> > any.
>
> Good point.  Here is a simple one where you have a table of keys and
> values, and you want to create a mapping built from that table:
>
>    Map : constant Mapping := (for I in Table'Range, Table(I).Key =>
> Table(I).Value);

Thanks. Put that into the next draft. ;-)

> > ---
> >
> >> A.18.2 The Generic Package Containers.Vectors
> >>
> >> Add the Aggregate aspect to the existing ones on type Vector:
> >>
> >>  type Vector is tagged private
> >>     with Constant_Indexing => Constant_Reference,
> >>          Variable_Indexing => Reference,
> >>          Default_Iterator  => Iterate,
> >>          Iterator_Element  => Element_Type,
> >>          Aggregate         => (Empty          => Empty_Vector,
> >>                                Add_Positional => Append);
> >
> > It might not be worth the effort, but it seems annoying that one
> > cannot write named array aggregates for Vectors. After all, the idea
> > is to make these as close to arrays as we can. It is especially
> > annoying that one can't write an iterator for these:
> >
> >      (for I in 1..10 => I)
> >
> > to initialize a Vector with the values 1 through 10. (Since we just
> > finally fixed this problem for regular array aggregates.)
>
> This is allowed.  You can use named notation even if there is no
> Add_Named.  An iterated component association just turns into a
> sequence of calls on Add_Positional.  I think that is pretty
> important.

OK, but I also might want to write:

    Is_Odd : Bool_Vector := (1 | 3 | 5 | 7 | 9 => True, 0 | 2 | 4 | 6 | 8 => False);

Again, the goal is to get a Vector aggregate to be as close to an array
aggregate as possible.

Also, this seems to give a preverse incentive to use iterators because other
forms of name aren't allowed:

    Is_Prime : Bool_Vector := (for I in 1..1 => False, for I in 2..3 => True,
                               for I in 4..4 => False, for I in 5..5 => True);

You *know* people are going to write this, because you're not allowing them to
write what they want.

> > One possibility would be to define an Add_Named for this purpose,
> > although we'd need a new length-expanding Insert operation for the
> > purpose (since the order isn't defined). With that change, named
> > aggregates could be written, but without ranges (other than in
> > iterators), and without a check for missing elements (which seems
> > like a bridge too far for this aspect in any case).
>
> I thought about that, but it seems kludgey.  I prefer just allowing
> the use of Add_Positional as illustrated above.
> Perhaps the wording needs to be clarified that, in the absence of
> Add_Named, you can still use iterated component associations, so long
> as there are no key_expressions.

Honestly, that seems like much more of a kludge to me. The existence of "=>" is
what changes an aggregate from positional to named, and I'd hate to lose that
property. (I know there's a hack allowing "others =>" in positional aggregates,
but that also feels wrong. Again, two wrongs don't make a right.) Morevoer, the
use of indexes as keys seems natural (what else are they if not a predefined
kind of key)?

Now, a real kludge here would be to give "keys" of discrete types special
properties, specifically that
    A .. B => ...
is equivalent to
    for I in A .. B => ...
if the key type is discrete.

Then a named vector aggregate could look just like a named array aggregate (sans
"others", which can't make sense in this context, as there is nowhere to get a
Length from). To make the equivalence close to perfect, we'd need a way to make
a completeness check, but that seems hard and somewhat unnecessary.

> ...

****************************************************************

From: Raphaël Amiard
Sent: Friday, January 26, 2018  8:46 AM

So I said I would report on C++ facilities for creating container literals.

After some studying it turns out that it is not really relevant:

1. C++ lacks something akin to the aggregate syntax. All they have are
   initializer lists, which are much more primitive and crude.

2. Their system extensively uses C++ templates, and their implicit
   instantiation ability. So it is not a good basis for us.

3. Our proposal is more advanced in terms of features already.

That's it !

****************************************************************

From: Tucker Taft
Sent: Monday, January 29, 2018  10:18 AM

Here is an example missing from the AI, showing how key_expression is used for
specifying a map, when the iterator loop parameter is *not* the key to use:

   Map : constant Mapping := (for I in Table'Range, Table(I).Key => Table(I).Value);

****************************************************************

From: Tucker Taft
Sent: Saturday, February 10, 2018  12:12 PM

Here is another update to the write-up of container aggregates.  I
re-organized and enhanced the examples a bit.  In the !discussion section,
I added a paragraph about why we are *not* extending the generalized
iterators to array aggregates, and also discussed the syntax of "map"
container aggregates when the key is specified explicitly in the presence
of an iterator.

Here is a cut-and-paste, followed by an attachment.  Hopefully one of them
comes through legibly. [This is version /03 of the AI - Editor.]

****************************************************************

From: John Barnes
Sent: Sunday, February 11, 2018  8:57 AM

This looks pretty good to me. No sign of excessive function expressions. Or if
there is, it has been adequately filtered by a bottle of decent claret.

****************************************************************

From: Tucker Taft
Sent: Sunday, February 11, 2018  10:52 AM

Thanks, John.  Sounds like you should share some of that claret with the rest
of the ARG... ;-)

****************************************************************

From: Tucker Taft
Sent: Sunday, February 11, 2018  10:30 AM

Randy pointed out that for an array aggregate, we could avoid actually getting
the values of a generalized iterator, but instead just get cursors, and count
the number of values to be produced that way.  An applicable index constraint
could also provide the bounds.  Even if there were a filter, you could ignore
the filter for the purposes of creating a temporary to hold the values, and
determine the final bounds after you fill in the potentially overly large
temp.  So perhaps the advantage of treating things like arrays and vectors
symmetrically argues for allowing array aggregates to use generalized
iterators.  I will write that up as a separate AI.

****************************************************************

From: Erhard Ploedereder
Sent: Monday, February 12, 2018  4:18 PM

> Finally, it is also a common pattern to combine multiple containers
> into one. Three approaches are supported. .....
>
> This is the sequential (or "list") one:
>
>    V : My_Set := (for A of X => A,
>                   for A of X => -A);

I would feel happier if sequentiality were expressed with ";" or "&" not ",".

Without explanation, I would have guessed that the construct above yields a
list of tuples (A, -A).

So, for the sequential case, my suggestion is
    V : My_Set := (for A of X => A;
                   for A of X => -A);
or even preferred:
    V : My_Set := (for A of X => A) & (for A of X => -A);

****************************************************************

From: Tucker Taft
Sent: Monday, February 12, 2018  4:32 PM

I believe "," is already allowed in array aggregates with two
iterated_component_associations, so it would be a bit odd to switch to ";"
here for container aggregates.

Of course "&" is allowed, presuming "&" is defined for the container type,
though you might end up requiring a type qualifier to resolve it, if you
provide the usual four definitions for "&" (i.e. C & C, C & E, E & C, E & E)
and the element type is composite.

****************************************************************

From: Jeff Cousins
Sent: Wednesday, February 14, 2018  1:41 PM

Thanks Tuck, that’s more comprehensible than some of the reduce stuff.

A few comments.

In the syntax,

positional_container_aggregate ::= (expression, expression {, expression)

is missing the closing }

In the examples, as String is indefinite it needs to be an
Indefinite_Ordered_Map (the reference is actually correct), and the separator
should be , not ; in the parameter list.

   package Int_String_Maps is
     new Ada.Containers.Indefinite_Ordered_Maps  -- see A.18.14
       (Key_Type => Integer, Element_Type => String);

Extending a tagged type requires an extension

   type Map_Type is new Int_String_Maps.Map with null record;

The following is a “downward conversion”:

   Empty_Map : constant Map_Type := Map_Type (Int_String_Maps.Empty_Map);

****************************************************************

From: Tucker Taft
Sent: Wednesday, February 14, 2018  2:39 PM

Thanks for the corrections.

****************************************************************

From: Tucker Taft
Sent: Wednesday, February 14, 2018  2:50 PM

Here is an update, with Jeff's corrections. [This is version /04 of the AI
- Editor.]

****************************************************************

From: Randy Brukardt
Sent: Tuesday, February 27, 2018  5:56 PM

> Here is an update, with Jeff's corrections.

I'm just getting around to reading this. Comments:

For some reason, you numbered this "version 5" (and the previous one "version
4"), but there never was a version 3. So you are off by one for the version
numbers. (This one was posted as version /04.)

---

The syntax for aspect Aggregate separates the parts with commas, but the
examples use semicolons. (I'd expect commas to be consistent with similar
syntax elsewhere in Ada, but whatever it is should be consistent.)

---

13.1.1 gives a list of things that can be an aspect. This new syntax for
aspect Aggregate is not one of those things. Note that when I was faced with
a similar issue for aspect Stable_Properties, I extended the syntax in 13.1.1.
We do have a blanket "alternative rules may apply" statement, but it would be
best if we made all of the language-defined aspects as consistent as possible.
One possibility would be to make "aggregate" a syntactic choice here, and then
redo Stable_Properties in terms of that (stable properties needing a list, for
which positional aggregate syntax would be fine).

Maybe we should do that here, maybe in another AI.

---

You and I have swapped positions on the iterated choices. :-) I now think that
they ought to be treated like others (which isn't allowed here) -- that is,
they can appear in positional aggregates, but only if they appear last. That's
the only way to make vector and array aggregates near identical, because the
iterated choice (except for the "traditional" discrete range one) doesn't
provide the indexes needed for a named array aggregate. And all of them (again
except for the "traditional" discrete range one) can't appear in a named array
aggregate without a key expression (which is really the index expression for
an array aggregate).

We probably need to figure out the array aggregate rules for this, because it
doesn't do for container aggregates and array aggregates to work very
differently in this case (or any case, when it comes to vector aggregates).

---

Erhard complained about using "," to represent sequentiality -- but this is an
aggregate, and the choices can be evaluated in an arbitrary order. Ergo, it
ISN'T representing sequentiality, just proximity. ";" (which does represent
sequentiality) would be wrong.

---

If we want array and vector aggregates to really be similar, then we need a
named vector aggregate option. This would best be somewhat separate from the
map aggregate, so that the requirements on named array and vector aggregates
are identical.

Ergo, I'd suggest a third choice (maybe "Add_Indexed") which requires that the
key type is discrete; then choices that are ranges would be allowed, and
non-overlap checking (with the only one dynamic choice rule) could be
enforced. Essentially, the legality rules would match an array aggregate sans
"others" (no others as there is no sane way to get an applicable index
constraint). (The dynamic rules would be essentially the same as Add_Named).

---

That's all I have at this point.

****************************************************************

From: Tucker Taft
Sent: Wednesday, February 28, 2018  6:05 AM

>> Here is an update, with Jeff's corrections.
>
> I'm just getting around to reading this. Comments:
>
> For some reason, you numbered this "version 5" (and the previous one
> "version 4"), but there never was a version 3. So you are off by one
> for the version numbers. (This one was posted as version /04.)

Sorry about that.

>
> ---
>
> The syntax for aspect Aggregate separates the parts with commas, but
> the examples use semicolons. (I'd expect commas to be consistent with
> similar syntax elsewhere in Ada, but whatever it is should be
> consistent.)

Oops.  Definitely meant to use "," since we want these to look like aggregates.
>
> ---
>
> 13.1.1 gives a list of things that can be an aspect. This new syntax
> for aspect Aggregate is not one of those things. Note that when I was
> faced with a similar issue for aspect Stable_Properties, I extended
> the syntax in 13.1.1. We do have a blanket "alternative rules may
> apply" statement, but it would be best if we made all of the
> language-defined aspects as consistent as possible. One possibility
> would be to make "aggregate" a syntactic choice here, and then redo
> Stable_Properties in terms of that (stable properties needing a list, for
> which positional aggregate syntax would be fine).
>
> Maybe we should do that here, maybe in another AI.

I would do it here.  Interdependent AIs are a bit of a pain.

> ---
>
> You and I have swapped positions on the iterated choices. :-) I now
> think that they ought to be treated like others (which isn't allowed
> here) -- that is, they can appear in positional aggregates, but only if they
> appear last. That's the only way to make vector and array aggregates near
> identical, because the iterated choice (except for the "traditional"
> discrete range
> one) doesn't provide the indexes needed for a named array aggregate.
> And all of them (again except for the "traditional" discrete range
> one) can't appear in a named array aggregate without a key expression
> (which is really the index expression for an array aggregate).

I am not really following this.  Could you give an example or two?

> We probably need to figure out the array aggregate rules for this,
> because it doesn't do for container aggregates and array aggregates to
> work very differently in this case (or any case, when it comes to vector
> aggregates).
>
> ---
>
> Erhard complained about using "," to represent sequentiality -- but
> this is an aggregate, and the choices can be evaluated in an arbitrary
> order. Ergo, it ISN'T representing sequentiality, just proximity. ";"
> (which does represent sequentiality) would be wrong.

I agree -- "," is used to separate elements in an aggregate, whether they are
iterated component associations or simple "<range> => value" associations.

>
> ---
>
> If we want array and vector aggregates to really be similar, then we
> need a named vector aggregate option. This would best be somewhat
> separate from the map aggregate, so that the requirements on named
> array and vector aggregates are identical.
>
> Ergo, I'd suggest a third choice (maybe "Add_Indexed") which requires
> that the key type is discrete; then choices that are ranges would be
> allowed, and non-overlap checking (with the only one dynamic choice
> rule) could be enforced. Essentially, the legality rules would match
> an array aggregate sans "others" (no others as there is no sane way to
> get an applicable index constraint). (The dynamic rules would be essentially
> the same as Add_Named).

I agree -- Add_Indexed sounds useful, and should be distinct from Add_Named.

> ---
>
> That's all I have at this point.

Thanks.  I'll try to send out a quick update, with at least some of this.

****************************************************************

From: Tucker Taft
Sent: Wednesday, February 28, 2018  4:58 PM

Steve Baird wrote (privately):

> Would any mention of container delta aggregates be premature?

Interesting.  That sort of makes sense.  You'd want that in a post-condition.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, February 28, 2018  5:44 PM

I'd suggest treating that as a separate AI, simply to avoid loading too much
stuff into a single AI. (Unless it turns out to be effectively trivial.) But
it does seem like it fits into the notion that a Vector aggregate and an array
aggregate are as similar in functionality as we can make them.

Note that since delta aggregates only work for named notation, such container
aggregates would only be meaningful for "Add_Named" and "Add_Indexed" cases.
Meaning that Lists and Sets couldn't use them -- annoying, but understandable.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, February 28, 2018  6:08 PM

...
> > You and I have swapped positions on the iterated choices. :-) I now
> > think that they ought to be treated like others (which isn't allowed
> > here) -- that is, they can appear in positional aggregates, but only
> > if they appear last.
> > That's the only way to make vector and array aggregates near
> > identical, because the iterated choice (except for the "traditional"
> > discrete range
> > one) doesn't provide the indexes needed for a named array aggregate.
> > And all of them (again except for the "traditional" discrete range
> > one) can't appear in a named array aggregate without a key
> > expression (which is really the index expression for an array aggregate).
>
> I am not really following this.  Could you give an example or two?

Array aggregates in Ada have some restrictions on them in order to ease
implementation, for instance the only dynamic part of a positional aggregate
has to be the last one, and there can only be a single dynamic part in a named
aggregate. I've been assuming that we'd want to preserve some version of this
going forward (so we aren't allowing arbitrary dynamic iterators in array
aggregates) -- if we wanted to relax these sorts of restrictions, we
definitely need to do that generally and not just in this one special case.
(The effort to implement is about the same for all of these cases.)

Anyway, container iterators used in an array aggregate have to be treated as
part of a positional aggregate, as they don't provide any index values (so
they *can't* be a named aggregate). For example:

      Some_List : ... -- A list container of Natural.

      type Some_Array is array (Positive range <>) of Natural;

      Agg1 : Some_Array := (for E of Some_List => E);
          -- This has to be treated as a positional array aggregate as the
          -- choice provide no array indexes. "others" isn't allowed here,
          -- so we can't even get them from the environment.

      Agg2 : Some_Array := (for I in Agg1'Range => Agg1(I));
          -- This is currently a named aggregate, since it is dynamic it can
          -- be the only choice here.
      Agg3 : Some_Array := (for E of Agg1 => E;
          -- This could go either way, as the indexes could be implied from
          -- those of Agg1, or ignored (since they're not explicit)
      Agg4 : Some_Array := (for C in Some_List.Iterator => Some_List.Element(C));
          -- Has to be positional, again there is no source for array indexes.

      Agg5 : Some_Array := (1, 2, 3, for E of Some_List => E);
          -- A positional aggregate, but no harder to implement than the
          -- existing cases.
      Agg6 : Some_Array := (1, 2, for E of Some_List => E, 4, 5);
          -- A positional aggregate, but with the dynamic part in the middle.
          -- Ugh. Readability isn't good, either (the trailing choices get
          -- lost). Ada currently doesn't allow this.
      Agg7 : Some_Array := (1 => 2, 2 => 4, for E of Some_List => E);
          -- Hopefully illegal, as the bounds of the iterator aren't defined.
          -- To make this work, we'd have to "imply" the bounds from the other
          -- choices. But that would require making the unordered (a named
          -- array aggregate) ordered in some sense. And then what to do if
          -- there aren't other choices available?

So, we either have to treat these iterators similarly to "others" and allow
them (only?) in positional aggregates, or treat them exactly like "others"
and allow them only when the bounds come from context. I preferred the first
of these when I thought about it, because it would likely to be annoying to
have to pre-figure the bounds (and declare an appropriate subtype) when these
aggregates are passed as parameters (with unconstrained formal types) or when
used in preconditions/postconditions (declaring an bounded subtype might not
be possible if other parameters are involved).

However this works for arrays, container aggregates ought to be similar, just
so we aren't giving unintended incentives to use containers rather than arrays
(or vice versa).

****************************************************************

From: Tucker Taft
Sent: Thursday, March 1, 2018  8:37 AM

Here is a new version [this is version /05 of the AI - ED.] that incorporates
Randy's idea of "indexed" aggregates,
where they require a contiguous no-duplicates range of indices in a named
aggregate.  This was more work than I originally anticipated, because we need
to create the object in advance with bounds determined from the names, and
then assign individual values.  I introduced the term "indexed aggregates" to
avoid having to keep repeating the circumstances under which the new
New_Indexed/Assign_Indexed routines would be invoked.

The net result of this is that now you can use named aggregates with Vectors,
and get the same level of checking as is provided for a named array aggregate
(full coverage, etc.).

I did *not* try to incorporate Randy's idea of positional aggregate with a
single iterated-element-association at the end.  It seems easy enough to
require the whole aggregate to be named in that case.  A mixture of positional
and named just seemed too weird, except for the special case of
"others => blah" which depends on the "applicable index constraint" notion
that doesn't generalize to container aggregates.

****************************************************************

From: Randy Brukardt
Sent: Thursday, March 1, 2018  10:19 PM

...
> I did *not* try to incorporate Randy's idea of positional aggregate
> with a single iterated-element-association at the end.  It seems easy
> enough to require the whole aggregate to be named in that case.  A
> mixture of positional and named just seemed too weird, except for the
> special case of "others => blah" which depends on the "applicable
> index constraint"
> notion that doesn't generalize to container aggregates.

But then you can't use them at all in "Add_Indexed" or array aggregates,
because they do not have any way to have bounds. Unless you plan to define
a whole new set of bounds rules -- and even that would have to limit them to
one iterator per aggregate (I sure hope we're not planning to dump the "one
dynamic choice" rule only in this weird case). That doesn't seem to be the way
you wanted to go -- but perhaps you've decided to abandon iterators in
aggregates except in the map case???

****************************************************************

From: Randy Brukardt
Sent: Thursday, March 1, 2018  11:03 PM

> Here is a new version ...

Now for a few comments on the actual text:

>So, to summarise:
>* New aspect on types specifying an empty default, plus an
>  Add_Positional, and either an Add_Named procedure or Assign_Indexed
>  procedure (with a New_Indexed function to go with it).
>* Extend aggregate syntax to trigger these

This second bullet seems like 1/2 of a thought. "trigger these" what? There
doesn't seem to be any extension of aggregate syntax needed here (that should
happen in array aggregates, as previously discussed, and should really happen
first so we don't end up with a hodge-podge of rules).

>Modify 13.1.1(5/3):
>  An aspect_mark identifies an aspect of the entity defined by the
>  associated declaration (the associated entity); the aspect denotes an
>  object, a value, an expression{, an aggregate}, a subprogram, or some
>  other kind of entity. If the aspect_mark identifies:

Don't we need to add "aggregate" to the syntax? It's rather confusing to have
it implicit as part of expression and we already have "name" which also
completely overlaps with "expression".

Side question: should we go back and change the syntax of Stable_Properties to
an aggregate? Or just leave well enough alone??

>   key_expression_list ::= /key_/expression {| /key_/expression}

No discrete_ranges here for the case of an indexed aggregate (array aggregates
definitely allow that). Intentional (and why?) or mistake?

There really is a lot of examples; it seems like too many for the RM. (This is
inverse of the usual problem.) I have to wonder if we need all of the
equivalences in the RM (as opposed to in the !examples section of the AI). But
we do need *some* examples in the RM.

****************************************************************

From: Tucker Taft
Sent: Friday, March 2, 2018   3:00 AM

> ...
>> I did *not* try to incorporate Randy's idea of positional aggregate
>> with a single iterated-element-association at the end.  It seems easy
>> enough to require the whole aggregate to be named in that case.  A
>> mixture of positional and named just seemed too weird, except for the
>> special case of "others => blah" which depends on the "applicable
>> index constraint"
>> notion that doesn't generalize to container aggregates.
>
> But then you can't use them at all in "Add_Indexed" or array
> aggregates, because they do not have any way to have bounds.

Not sure what you mean by "them" in "can't use them."  Did you mean vectors?
The proposal does support them, but with a precondition that the low bound
matches Index_Type'First.

> Unless you plan to define a
> whole new set of bounds rules -- and even that would have to limit
> them to one iterator per aggregate (I sure hope we're not planning to
> dump the "one dynamic choice" rule only in this weird case). That
> doesn't seem to be the way you wanted to go -- but perhaps you've
> decided to abandon iterators in aggregates except in the map case???

Did you look at the proposal?  If so, please clarify your concern.  If not,
please take a look at the proposal for how Vectors would be supported, and if
you have comments on that, let me know.  Clearly some array-like containers
might allow arbitrary bounds, and others, such as Vector, would require the
same low bound for all objects of a given type.  I presumed a precondition
could be used to enforce the vector-like requirement of a fixed low bound.

****************************************************************

From: Randy Brukardt
Sent: Friday, March 2, 2018  6:01 PM

...
> Not sure what you mean by "them" in "can't use them."  Did you mean
> vectors?  The proposal does support them, but with a precondition that
> the low bound matches Index_Type'First.

"them" = iterated-element-association.

I've mostly been thinking about how they fit into the array aggregate rules.
Once that is figured out, then one can use similar rules for container
aggregates. Starting with the containers is ass-backwards -- that's a blank
slate, whereas with the arrays they have to be fit into existing wording.

As I've previously noted, I do not want incentives to use one kind of type
over the other (in either direction), so anything available in the containers
also has to be available in array aggregates.

Aside: The first paragraph of the Legality Rules just provides definitions.
Shouldn't that be in Static Semantics?

> > Unless you plan to define a
> > whole new set of bounds rules -- and even that would have to limit
> > them to one iterator per aggregate (I sure hope we're not planning
> > to dump the "one dynamic choice" rule only in this weird case). That
> > doesn't seem to be the way you wanted to go -- but perhaps you've
> > decided to abandon iterators in aggregates except in the map case???
>
> Did you look at the proposal?  If so, please clarify your concern.  If
> not, please take a look at the proposal for how Vectors would be
> supported, and if you have comments on that, let me know.

The idea that a named aggregate uses Add_Positional is bizarre. It works here
because of the blank-slate issue, but it will not work with arrays.

Well, actually it doesn't work that well here. You now have the possibility of
mixed keyed and unkeyed choices (because you are treating an iterator as named
even though it doesn't provide any). For instance:

    A_Map := ("Tuck" => True, "Randy" => False, for E of Some_Bits => E);

Assuming that both Add_Named and Add_Positional are supported, this
"aggregate" would be implemented with a mix of "named" and "positional"
inserts - that seems like madness. (A type supporting both could make sense
for a keyed set, for instance.)

It's also bizarre (and violates my rule above), that you can write:

    A_Map := (for E of Some_Bits => E, for E of Some_Other_Bits => E);

for a map, but it's illegal for an array or vector.

And it's bizarre that you can write:

    A_List := (for I in 1..1 => True, for I in 1..1 => False, for E of Some_Bits => E);

but you're not allowed to write:

    A_List := (True, False, for E of Some_Bits => E);

(since lists are only positional, and the only named choices that a positional
type can write are iterators). If we adopted these rules as-is, I predict
you'll see the above pretty often.

> Clearly some array-like containers might allow arbitrary bounds, and
> others, such as Vector, would require the same low bound for all
> objects of a given type.  I presumed a precondition could be used to
> enforce the vector-like requirement of a fixed low bound.

I've said repeatedly that I would want to see how these iterators are
integrated into the array containers before worrying about anything else.
That makes the most sense as positional, at the very least in terms of how the
bounds are determined, the legality rules on the indexes are checked, and the
like. If you try to add them to named aggregates, you'll need to make a
special case for every rule that applies to such an aggregate ("unless it
contains an iterator, in which case the bounds are completely different").

If they end up being part of positional aggregates for arrays (definitely the
easiest way to deal with them), then they ought to be handled similarly for the
container aggregates. End of story.

****************************************************************

From: Tucker Taft
Sent: Friday, March 2, 2018  7:10 PM

>> Not sure what you mean by "them" in "can't use them."  Did
>> you mean vectors?  The proposal does support them, but with a
>> precondition that the low bound matches Index_Type'First.
>
> "them" = iterated-element-association.

Oh!  I wouldn't have guessed...

> I've mostly been thinking about how they fit into the array aggregate rules.
> Once that is figured out, then one can use similar rules for container
> aggregates. Starting with the containers is ass-backwards -- that's a blank
> slate, whereas with the arrays they have to be fit into existing wording.

Perhaps, but I don't see anyone screaming for more kinds of array aggregates,
whereas aggregates for containers seems like an obvious hole in the current
standard.

> As I've previously noted, I do not want incentives to use one kind of type
> over the other (in either direction), so anything available in the
> containers also has to be available in array aggregates.

Yeah, but vectors and arrays have one fundamental difference -- vectors are
extensible, but of course imply dynamic or bounded allocation.  So there is
already a pretty strong incentive to use the "right" one, and a few extra
syntactic alternatives for the aggregates of one over the other seems OK
given that they have this fundamental difference in functionality.

> Aside: The first paragraph of the Legality Rules just provides definitions.
> Shouldn't that be in Static Semantics?

We have allowed definitions to be almost anywhere as far as I know, and
certainly we allow them in legality rules.

> Unless you plan to define a
> whole new set of bounds rules -- and even that would have to limit
> them to one iterator per aggregate (I sure hope we're not planning to
> dump the "one dynamic choice" rule only in this weird case). That
> doesn't seem to be the way you wanted to go -- but perhaps you've
> decided to abandon iterators in aggregates except in the map case???

Did you look at the proposal?  If so, please clarify your
concern.  If not, please take a look at the proposal for how
Vectors would be supported, and if you have comments on that,
let me know.

> The idea that a named aggregate uses Add_Positional is bizarre. It works
> here because of the blank-slate issue, but it will not work with arrays.

I don't see the issue.  As pointed out, arrays and vectors are not the same
thing, fundamentally because you can add elements to a vector, and you cannot
add elements to an array.  I think you are over-constraining the solution by
requiring them to have identical capabilities.  I agree they should be as
similar as possible, but we should not have to sacrifice the functionality of
one just because non-extensible arrays can't handle them.

> Well, actually it doesn't work that well here. You now have the possibility
> of mixed keyed and unkeyed choices (because you are treating an iterator as
> named even though it doesn't provide any). For instance:
>
>   A_Map := ("Tuck" => True, "Randy" => False, for E of Some_Bits => E);
>
> Assuming that both Add_Named and Add_Positional are supported, this
> "aggregate" would be implemented with a mix of "named" and "positional"
> inserts - that seems like madness. (A type supporting both could make sense
> for a keyed set, for instance.)

That was not my intent.  The rules should probably disallow this mixing.  In
fact, now that we have New_Indexed/Assign_Indexed, we could probably disallow
having both Add_Named and Add_Positional for the same type.   Set-like types
would use Add_Positional, and Map-like types would use Add_Named, and
Vector-like types could have both Add_Positional and Assign_Indexed.  I can't
imagine now a type that would require having both Add_Named and Add_Positional.
Even a "keyed set" doesn't gain anything from using named notation, and in fact
giving names just creates trouble since you would require that the name matches
the key embedded in the value.  So I would say we should disallow that
combination for now.  If someone eventually comes up with an important use for
it, we could add it back.

> It's also bizarre (and violates my rule above), that you can write:

Let's not talk about "rules" yet...

>   A_Map := (for E of Some_Bits => E, for E of Some_Other_Bits => E);
>
> for a map, but it's illegal for an array or vector.

Not sure why you are saying the above is illegal for a vector.  Add_Positional
works for a vector.

> And it's bizarre that you can write:
>
>   A_List := (for I in 1..1 => True, for I in 1..1 => False,
>              for E of Some_Bits => E);
>
> but you're not allowed to write:
>
>   A_List := (True, False, for E of Some_Bits => E);

This is mixing positional and named, which is not something we have allowed
in the past.  I can understand your interest in this, but not supporting it
is not "bizarre" in my view.

> (since lists are only positional, and the only named choices that a
> positional type can write are iterators). If we adopted these rules as-is, I
> predict you'll see the above pretty often.

Another option is to just rely on concatenation:

  A_List := (True, False) & (for E of Some_Bits => E);

which doesn't seem so bad.

Clearly some array-like containers might allow
arbitrary bounds, and others, such as Vector, would require
the same low bound for all objects of a given type.  I
presumed a precondition could be used to enforce the
vector-like requirement of a fixed low bound.

> I've said repeatedly that I would want to see how these iterators are
> integrated into the array containers before worrying about anything else.

I think you are beginning to get a bit autocratic here.  I understand your
desire to keep nearly equivalent functionality, but you are starting to sounds
like these are uncompromisable demands.   Let's keep this to "in my view..."
or something.  The veto-like terminology gets old...

> That makes the most sense as positional, at the very least in terms of how
> the bounds are determined, the legality rules on the indexes are checked,
> and the like. If you try to add them to named aggregates, you'll need to
> make a special case for every rule that applies to such an aggregate
> ("unless it contains an iterator, in which case the bounds are completely
> different").
>
> If they end up being part of positional aggregates for arrays (definitely
> the easiest way to deal with them), then they ought to be handled similarly
> for the container aggregates. End of story.

Again, there are multiple views here.  I am happy to be part of a search for
solutions that make us all happy, but lets not start making threats.  It
really doesn't help.

****************************************************************

From: Randy Brukardt
Sent: Saturday, March 3, 2018  11:06 PM

>> I've mostly been thinking about how they fit into the array aggregate rules.
>> Once that is figured out, then one can use similar rules for
>> container aggregates. Starting with the containers is ass-backwards
>> -- that's a blank
>> slate, whereas with the arrays they have to be fit into existing wording.

> Perhaps, but I don't see anyone screaming for more kinds of array aggregates,
> whereas aggregates for containers seems like an obvious hole in the current
> standard.

Doesn't seem that "obvious" to me; the best feature of aggregates is the
completeness checks but those don't make any sense for containers. Thus,
container aggregates seem logical but hardly critical.

>> As I've previously noted, I do not want incentives to use one kind of type
>> over the other (in either direction), so anything available in the
>> containers also has to be available in array aggregates.

> Yeah, but vectors and arrays have one fundamental difference -- vectors are
> extensible, but of course imply dynamic or bounded allocation.  So there is
> already a pretty strong incentive to use the "right" one, and a few
> extra syntactic alternatives for the aggregates of one over the other
> seems OK given that they have this fundamental difference in functionality.

???  Extensibility doesn't have much to do with aggregates, because they are
an all-at-once construction of an object. And arrays can be unconstrained, so
aggregates with variable lengths make sense for both. Thus, they seem like two
views of the same thing to me.

>> Aside: The first paragraph of the Legality Rules just provides definitions.
>> Shouldn't that be in Static Semantics?

> We have allowed definitions to be almost anywhere as far as I know,
> and certainly we allow them in legality rules.

That paragraph seems out of place to me, as it has nothing to do with
legality. (And not much to do with the actual legality rules, either.) When
I wrote that, I thought there was a Static Semantics section, and I thought
the definition fit better there. But I can't find that section now, so I
apparently imagined that.

...
>> The idea that a named aggregate uses Add_Positional is bizarre. It
>> works here because of the blank-slate issue, but it will not work with arrays.

> I don't see the issue.

Obviously. :-)

> As pointed out, arrays and vectors are not the same thing,
> fundamentally because you can add elements to a vector, and you cannot
> add elements to an array.

Which has precisely nothing to do with a construct for constructing an object
from scratch.

> I think you are over-constraining the solution by requiring them to
> have identical capabilities.  I agree they should be as similar as
> possible, but we should not have to sacrifice the functionality of one
> just because non-extensible arrays can't handle them.

Who said anything about "sacrificing" anything? All I've said is that I want
the rules to be essentially the same for both. That doesn't require any
functionality to be dropped.

...
>> It's also bizarre (and violates my rule above), that you can write:
>
>	Let's not talk about "rules" yet...

Substitute "principle" then. Sheesh.

>>
>>	   A_Map := (for E of Some_Bits => E, for E of Some_Other_Bits =>
E);
>>
>>	for a map, but it's illegal for an array or vector.
>
> Not sure why you are saying the above is illegal for a vector.
> Add_Positional works for a vector.

You have a Legality Rule that says that an iterator can only occur once for
a indexed aggregate type. That seems to prevent the above. (If it doesn't,
I'd object even louder, because the form of an iterator certainly shouldn't
change the legal uses.)


>>And it's bizarre that you can write:
>>
>>   A_List := (for I in 1..1 => True, for I in 1..1 => False,
>>              for E of Some_Bits => E);
>>
>>		but you're not allowed to write:
>>
>>   A_List := (True, False, for E of Some_Bits => E);
>
>This is mixing positional and named, which is not something we have
>allowed in the past.  I can understand your interest in this, but not
>supporting it is not "bizarre" in my view.

You've missed my point: people WILL write the former because they aren't
allowed to write the latter. That's what's "bizarre". I'd be happy to make
both of them illegal -- it just doesn't make sense to allow a really complex
way to write something and not allow a much simpler way to write the same
thing.

>>(since lists are only positional, and the only named choices that a
>>positional type can write are iterators). If we adopted these rules
>>as-is, I >>predict you'll see the above pretty often.

>Another option is to just rely on concatenation:
>
>	  A_List := (True, False) & (for E of Some_Bits => E);
>
>	which doesn't seem so bad.

...except the lists don't have "&". And even if they did have "&", the above
requires creating three objects and copying all of the contents of two of
them. That's a lot of unnecessary overhead.

...
>>	I've said repeatedly that I would want to see how these iterators are
>>	integrated into the array containers before worrying about anything else.
>
>	I think you are beginning to get a bit autocratic here.  I understand your
> desire to keep nearly equivalent functionality, but you are starting to sounds
> like these are uncompromisable demands.   Let's keep this to "in my view..." or
> something.  The veto-like terminology gets old...

Huh? I said "I would want", which is a fact. You can ignore that "want" if you
like, but I don't view this as something that I am likely to compromise much
on.

Moreover, it's possible that having worked out the array rules, we'll find
that it makes more sense to structure the aggregates as you have proposed. I
I just want to have that (array rules) done first, before we finalize too much
of these container rules. They make the organization of the container rules be
more sensible.

>>That makes the most sense as positional, at the very least in terms of
>>how the bounds are determined, the legality rules on the indexes are
>>checked, and the like. If you try to add them to named aggregates,
>>you'll need to make a special case for every rule that applies to such
>>an aggregate ("unless it contains an iterator, in which case the
>>bounds are completely different").
>>
>>If they end up being part of positional aggregates for arrays
>>(definitely the easiest way to deal with them), then they ought to be
>>handled
similarly
>>for the container aggregates. End of story.
>
>Again, there are multiple views here.  I am happy to be part of a search for
>solutions that make us all happy, but lets not start making threats.
>It really doesn't help.

I don't see any threat here, certainly wasn't making one. I have just said
repeatedly that I want to see how the new iterators fit into array aggregates
(which already have a pile of rules) before finalizing the container rules
(which are more of a blank page). Otherwise, we're likely to end up with
something that is more different than necessary.

I agree that having the iterators be "positional" is somewhat weird -- it
was my complaint with your first draft of this AI. But more thought on the
subject shows that that is the only realistic possibility (which you
implicitly agree with by having them use the Add_Positional routine). I also
realized that we already have "others => expr" in the array positional cases,
so it hardly would be unusual to have more such cases.

Array aggregates are syntactically divided into positional and named (indexed
for the containers) cases, and the rules are almost all different for the two
cases. I find the mixing in your current container proposal (where the syntax
is divided, but then the rules end up being blended together in an almost
arbitrary manner) uncomfortable. On top of which, it prevents reasonable
container aggregates unless someone writes them with phony iterators. How does
that help?

****************************************************************

From: Randy Brukardt
Sent: Friday, March 9, 2018  11:31 PM

A couple of extra thoughts on this, noted when filing these messages tonight:

> Tucker writes:
>
> >> I've mostly been thinking about how they fit into the array
> >> aggregate rules. Once that is figured out, then one can use similar
> >> rules for container aggregates. Starting with the containers is
> >> ass-backwards
> >> -- that's a blank slate, whereas with the arrays they have to be
> >> fit into existing wording.
>
> > Perhaps, but I don't see anyone screaming for more kinds of array
> > aggregates, whereas aggregates for containers seems like an obvious
> > hole in the current standard.

I responded:
> Doesn't seem that "obvious" to me; the best feature of aggregates is
> the completeness checks but those don't make any sense for containers.
> Thus, container aggregates seem logical but hardly critical.

I should point out that we're really discussing the iterator cases here. Most
of the "problem" to be solved by container aggregates are solved by simple
positional or mapped aggregates:
    A_List := (A, B, C);
    A_Map := ("Randy" => A, "Tucker" => B);

I have no problem with the portion of the solution involving them (it looks
fine to me).

OTOH, adding container iterators into this mix is not so obvious, neither in
terms of the need or in terms of how they fit into the existing kinds of
aggregates or even how they fit into the proposal itself.

Tucker has been ascribing nearly magical properties to various uses of these
iterators, in ways that tend to trigger my BS detector. That's tempered by the
fact that Tucker usually isn't the sort to rave on crazily. Still, anyone can
get over-enthusiastic about something (that's probably how I got here in the
first place ;-).

*IF* it was up to me solely, I would treat the solution to the obvious
container aggregate problem separate from the question of adding containers
iterators to *any* aggregates. And, the AI for that latter case, I would add
the iterators to both array and container aggregates at the same time. (If
it's good for the one, it has to be good for the other -- they're different
views of the same thing, the only difference being that arrays are built in.)
I think any other path for additional iterators to be folly for many reasons,
the most obvious being that creating an array from a container should be no
harder than creating a container from an array.

This shouldn't be considered a demand - if I'm the only one that feels this
way, I can take a hint...

Aside: I like how Tucker snuck in a notation for an empty container aggregate
into his container AI, despite already having another AI open for that, and
despite that other AI not getting much traction. I'm going to resist saying
more...

...

> > Yeah, but vectors and arrays have one fundamental difference --
> > vectors are extensible, but of course imply dynamic or bounded
> > allocation.  So there is already a pretty strong incentive to use
> > the "right" one, and a few extra syntactic alternatives for the
> > aggregates of one over the other seems OK given that they have this
> > fundamental difference in functionality.

I said:
> ???  Extensibility doesn't have much to do with aggregates, because
> they are an all-at-once construction of an object.
> And arrays can be unconstrained, so aggregates with variable lengths
> make sense for both. Thus, they seem like two views of the same thing
> to me.

To expand a bit, it's clear that the underlying implementation of containers
aggregates use operations to add the elements to the container one at a time.
That's where the extensibility comes into play. However, the programmer model
for an aggregate is that it is built all at once -- one hopefully is not
looking under the hood here to see how it *really* is implemented. Since the
"thought" model for an aggregate is something built as a single object all at
once, extensibility doesn't come into play.

It would come into play if you wrote a normal for loop statement to fill the
container:

       for E of Some_Array loop
           Append (New_Container, E);
       end loop;

but an aggregate iterator is logically done all at once in an arbitrary order;
there's no implied order involved. (Indeed, it would be a candidate for as-if
parallel operation if such a thing actually made sense on typical hardware.)

****************************************************************

From: Tucker Taft
Sent: Saturday, March 10, 2018  11:31 PM

> ...
> Aside: I like how Tucker snuck in a notation for an empty container
> aggregate into his container AI, despite already having another AI
> open for that, and despite that other AI not getting much traction.
> I'm going to resist saying more...

The more I worked on the "(null T)" idea for an empty aggregate of type T, the
less I liked it.  Since the container aggregate required the specification of
an "Empty" operation, it made sense to me to include a notation for an empty
aggregate as part of the container aggregate AI, and make it something that
didn't require the full name of the type to be buried in the aggregate.  We
generally don't allow you to look inside an aggregate until you know exactly
what type it is, so it seemed particularly silly to repeat the full name of
the type inside an empty aggregate.  Once we leave out the full name of the
type from the "empty" aggregate, the notation that kept seeming the most
obvious to me for things like "empty set" and "empty map" was "(<>)".

****************************************************************

From: Tucker Taft
Sent: Saturday, March 10, 2018  9:58 AM

> ...
> I should point out that we're really discussing the iterator cases here.
> Most of the "problem" to be solved by container aggregates are solved
> by simple positional or mapped aggregates:
>    A_List := (A, B, C);
>    A_Map := ("Randy" => A, "Tucker" => B);
>
> I have no problem with the portion of the solution involving them (it
> looks fine to me).
>
> OTOH, adding container iterators into this mix is not so obvious,
> neither in terms of the need or in terms of how they fit into the
> existing kinds of aggregates or even how they fit into the proposal itself. ...

We added iterators to array aggregates after 20 years, and they were sorely
missed.  The point is that you often want to create an aggregate where the
components are systematically a function of the position, or something else
that is varying.  Turning your logic around, it would be weird to allow the
following for an array aggregate but not for a vector aggregate:

   (for I in 1..N => I**2)

The *only* controversy seems to be whether we should require supporting
container iterators in array aggregates, and that seems to be only a
controversy for you.  Everyone else seems to see it as clear that we should
focus on allowing container iterators in container aggregates, since a
frequent use (in my experience, as well as the experience in using similar
constructs in Python and other languages), is that you want to create a new
container that is an element-wise function of some other container.  E.g.,
given a set, you want to create a new set that contains, say, the square of
each element, or the mapping of each element to some other type, ...

There is less need to construct an array as an element-wise function of a
container in my experience, and the added expense of having to pre-allocate
the array makes this less attractive from an implementation point of view.
I am happy to add this feature, but it feels like a separate issue, with
more complex implementation requirements, and I wouldn't want to "sink" the
container aggregate idea overall just because supporting an array as an
element-wise function of a container requires a more complex implementation.

****************************************************************

From: Randy Brukardt
Sent: Saturday, March 10, 2018  7:37 PM

...
> > OTOH, adding container iterators into this mix is not so obvious,
> > neither in terms of the need or in terms of how they fit into the
> > existing kinds of aggregates or even how they fit into the
> proposal itself. ...
>
> We added iterators to array aggregates after 20 years, and they were
> sorely missed.

Not by me. I think there were a handful of cases in my 40 years of programming
where they would have helped. Definitely a non-zero need, but hardly something
critical.

... The point is that you often want to
> create an aggregate where the components are systematically a function
> of the position, or something else that is varying.
> Turning your logic around, it would be weird to allow the following
> for an array aggregate but not for a vector aggregate:
>
>    (for I in 1..N => I**2)

Wouldn't mind this at all, so long as it follows array iterator rules -- that
is, it calls Add_Indexed in your current draft.

...
> The *only* controversy seems to be whether we should require
> supporting container iterators in array aggregates, and that seems to
> be only a controversy for you.  Everyone else seems to see it as clear
> ...

That is clearly your imagination; we've never discussed this topic at a
meeting so there is no possible way to know what "everybody else" is
thinking. My recollection was that this was introduced at a meeting as "what
we should do" without any real opportunity to digest it ahead of time.

...
> There is less need to construct an array as an element-wise function
> of a container in my experience,

How do you know this? We've never had such iterators in Ada, and other
languages that have such iterators treat arrays similarly to all of the
other containers -- so how could you tell the difference? An array is just a
container that is built-in for historical reasons, treating it differently is
madness.

---

In any case, if "everybody else" agrees with you, then you should clearly
ignore me. Which is essentially what you've been doing on this topic, so it
isn't worth any more of my time in this venue. (We can fight it out at
whatever meeting this AI gets discussed.)

To summarize, I can't support the current proposal for a myriad of reasons:

(1) The proposal is unnecessarily different than array aggregates: The
    iterators allowed and the interpretation is different for the arrays and
    the containers, ranges aren't allowed as indexed choices for no known
    reason, nor is <> allowed for the named forms (why a default initialized
    component is valuable for an array and not for a container escapes me).

(2) <> in an aggregate means "default initialize", and to use it to represent
    an empty container is a nasty collision of meanings in essentially the
    same construct. (Ideally, "<>" would mean default-initialization
    everywhere, but that was a bridge-too-far in the past...I don't want to
    prevent such a usage.)

(3) There's a separate AI for empty aggregates, and there is no good reason to
    put that into this one -- Empty_List is a fine way to get an empty list
    container. (Not to mention that writing such containers explicitly isn't
    going to happen very often in any case.) Besides, there is no sane way (not
    even a function) to write a 1-element list (as a positional-only aggregate,
    the only choice in the current AI is a phony iterator:
           My_List := (1); -- Not an aggregate.
           My_List := (for I in 1..1 => 1); -- OK, but insane.)
    So there is no real "consistency" argument for the need for empty
    aggregates.

(4) The AI implements "named" aggregates with the "positional"
    implementation (the Add_Positional subprogram). This weird mixing of
    concepts is very uncomfortable.

(5) The AI as written allows elements implemented by positional and named
    routines in the same aggregate. (You did promise to fix this.)

I'm not against this concept per-se, but I'm definitely against this
implementation of the concept.

****************************************************************

From: Tucker Taft
Sent: Sunday, March 11, 2018  10:47 AM

> ...
> I'm not against this concept per-se, but I'm definitely against this
> implementation of the concept.

I'll take another shot at this AI, and hopefully can reach some kind of middle
ground here.

I would point out that in many languages, (non-extensible) arrays and
(extensible) vectors are two different things, and treated quite differently.
But I'll try to make the aggregates more consistent between them in the next
version of the AI.

****************************************************************

From: Jeff Cousins
Sent: Monday, March 12, 2018  10:23 AM

>> We added iterators to array aggregates after 20 years, and
>> they were sorely missed.

>Not by me. I think there were a handful of cases in my 40 years of
> programming where they would have helped.

At least I agree with Tucker on this bit, I would have used almost daily.

>> The *only* controversy seems to be whether we should require
>> supporting container iterators in array aggregates, and that
>> seems to be only a controversy for you.  Everyone else seems
>> to see it as clear ...

>That is clearly your imagination; we've never discussed this topic at a
>meeting so there is no possible way to know what "everybody else" is
>thinking.

The only recent-ish messages seem to have been from Randy and Tucker.
Personally I’m confused as to what exactly is being proposed here – and I
suspect that it’s the same for some pf the other members, hence why they’re
not commenting – and would have to abstain if it came to a vote.

****************************************************************

From: Edmond Sconberg
Sent: Monday, March 12, 2018  11:15 AM

I share Jeff's sense of confusion,  I am all in favor of the proposed
container aggregates and the new attributes for indexed, named, and keyed
insertions.  I don’t know what the exact proposal is for new array aggregates.
I agree that it is worthwhile generalizing the use of container iterators
wherever a loop is implied. (the issue of the size of an array aggregate built
with an iteration over a container is an implementation question, the
point is that the notation is useful). If the context is an array type, and S
is a hashed set,  I can make sense of  (for X of S => F (X)),  but given
that the iteration over S produces values in some unspecified order I’ve
created an array with elements in unpredictable order. Not sure that this
is particularly useful. If we really need to produce indexable sequences
from other containers, vectors may be all we need.

Finally, I find  (<>) perfectly acceptable as a notation for an empty
container!

****************************************************************

From: Randy Brukardt
Sent: Monday, March 12, 2018  2:48 PM

> I share Jeff's sense of confusion,  I am all in favor of the proposed
> container aggregates and the new attributes for indexed, named, and keyed
> insertions.  I don’t know what the exact proposal is for new array
> aggregates.

I don't know what the exact proposal is either, because there hasn't been
one. Which is the core of my objection - the array case is harder to define
since it has to fit into the existing rules. So I'd like to see that before
getting too far into the containers aggregate proposal - it might make sense
to make the rules similar for the two cases.

****************************************************************

From: Tucker Taft
Sent: Monday, March 12, 2018  5:13 PM

A new version is forthcoming...

****************************************************************

From: Tucker Taft
Sent: Tuesday, March 13, 2018  5:23 PM

OK, here is another shot at container aggregates, including a section on
generalizing array aggregates. [This is version /06 of the AI - ED.]  I still
didn't allow having a positional container aggregate with an iterator part at
the end.  Still feels weird to me, but could be added later if the ARG as a
whole really wants it.

Other major changes -- now call the operation "Add_Unnamed" rather than
"Add_Positional," since it is used in both positional and named aggregates.
Don't allow both Add_Unnamed and Add_Named for the same type.  Allow "<>" in
"indexed" aggregates.  Allow discrete ranges in indexed aggregates.  Removed
a few of the overly complex examples.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, March 28, 2018  8:27 PM

> OK, here is another shot at container aggregates, including a section 
> on generalizing array aggregates.

Thanks. A few thoughts.

(1) I find this better, still not perfect (but when does that happen? :-)

(2) I still dislike using (<>) for empty aggregates, especially when <> means
something else. I won't belabor this, I've covered it before. [(7) covers one
possible way to eliminate this objection.]

(3) <> in array and record aggregates means a default-initialized component. 
Here, you have defined it to mean an empty (uninitialized) slot instead. These
aren't the same thing! (It's a Bounded Error to read an uninitialized slot; a
default-initialized element is usable in the normal way.) I use
default-initialized aggregate items all the time with the explicit intent of
getting default initialization (typically, so counters/lengths and the like
are initialized to empty). 

Note that most of the (definite) containers have Insert operations that don't
take an element and insert a default initialized object. (See, for instance,
A.18.2(163-165/2)). I don't know that it is worth explicitly using these
operations, but that fact that they were considered valuable enough to define
suggests to me at least that <> meaning default-initialization is also
valuable.

It is clear that <> is only meaningful for definite types (that is a
requirement of record and array types, not for containers).

So, as an alternative proposal, consider the following outline:
    * <> is only allowed when the element type is definite;
    * <> is allowed for any construct in a named aggregate;
    * For Add_Named, <> is equivalent to the block:
         declare
            Elem : Element_Type; -- Default-initialized;
         begin
            Add_Named (Container, Key, Elem);
         end;
    * For Add_Indexed, <> is equivalent to the block:
         declare
            Elem : Element_Type; -- Default-initialized;
         begin
            Add_Named (Container, Index, Elem);
         end;
    * If Add_Unnamed is needed, it would follow as the above.

We can discuss this further later.

(4) There's some typos in the array iterator proposal: the clause number is
incorrectly 4.2.3 in a couple of places, and I presume that "Add 4.2.3(20) as
follows:" should really be "Add after 4.3.3(20):" since I presume you are not
suggesting to replace 4.3.3(20) [and there is no 4.2.3 anyway].

(5) The wording change (presumably) added after 4.3.3(20) seems a bit more 
explicit than necessary. Do we have to *require* that the iteration is 
performed twice? We surely should allow it, but if the compiler can figure
the length some other way (or is willing/able to use an expandable temporary
space), shouldn't we allow only a single execution of the iterator? (An as-if
optimization isn't enough, as one can detect if a container iterator is
executed.)

In particular, if one uses the array element iterator in this way (surely
legal), the length can be figured from the array without executing any
iteration. That is:
     for E of Some_Array =>
the compiler can surely figure out how many elements there are without
executing anything.
Similarly, for the language-defined containers, the length of the container
element iterator can be figured by asking the container length without
iterating anything:
     for E of Some_Container =>
The number of elements in this case is always Some_Container.Length for the
language-defined containers.

This latter optimization would require some magic as it stands (although we
could define a language-defined enhancement if we wanted to make this a
standard thing). As an implementor, I'd probably define an
implementation-defined aspect and specify that with Length. Since iteration
can be expensive, that would be a better option for these common iterators.

Of course, arbitrary cursor-based iterators would probably require a count.
For something like:
    for C in Some_Container.Iterator (From => First, To => Last) loop
the number of elements is unknown.

(6) I didn't see anything to deal with the 1-element aggregate for containers
that only define Add_Unnamed. As we all know, there is no single element
positional aggregate. Containers that only define Add_Unnamed (like lists and
sets) can't fall back on named notation, as there are no names to use.

The only solution in the current AI is to use some sort of iterator:

         (for I in 1 .. 1 => The_Element)

If users think that saying "(1 .. 0 => The_Component)" is annoying for the
rarely-used null array aggregate, they ought to scream bloody murder for being
forced to write the above.

I'm definitely against defining any empty aggregate if we don't fix this
problem, as single element lists seem a lot more likely to me than empty
lists. If you have to use a function to sensibly create the single element
lists, then there cannot be any worse hardship to do the same for the less
likely empty lists. (That is, there isn't much point in plugging one hole if
we leave a worse one.)

I would personally prefer to add the missing functions to List and Set to 
create one-element lists and forget anything about empty aggregates (here and
for null arrays), but I'd be happy to figure better solutions.

(7) One wild thought I just had was that we don't really need an Empty
parameter to the Aggregate aspect -- all of the language-defined containers 
require the default-initialized container to be "empty". If we simply said
that the container object is default-initialized, this would mean "empty" for
the language-defined containers, then (<>) would actually mean a default
initialized container -- which would eliminate my primary objection to using
the notation for an empty container aggregate. (Sorry, this doesn't work for
other aggregates -- <> only is meaningful for definite types.) It also would
simplify the semantics description slightly - one less subprogram to check and
describe rules for.

A user-defined container that wanted to use the aggregate notation would have
to define the default-initialized state to be something useful as the start of
a container. Perhaps I'm lacking imagination here, but I can't see a sensible
way for a container to start with elements in it (where would they come from?)
and still have adding elements be particularly meaningful. So this suggestion
would reduce the flexibility a bit, but probably wouldn't make much difference
in practice.

****************************************************************

From: Tucker Taft
Sent: Wednesday, March 28, 2018  8:57 PM

>> OK, here is another shot at container aggregates, including a section 
>> on generalizing array aggregates.
> 
> Thanks. A few thoughts. ...

Thanks, all good comments, and no particular responses at this point.  It
feels like we are on the same wavelength, even if some of the details might
be up for debate.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, March 28, 2018  9:14 PM

Comments are welcome; I can justify debating this proposal's details much 
more easily than anything involving rarely-used contracts. ;-)

****************************************************************

From: Jeff Cousins
Sent: Saturday, July 7, 2018  3:52 PM

a few minor comments that I didn’t raise at the meeting as the AI was going 
back for a re-write anyway...

4.3.5

Dynamic Semantics

3rd bullet – “as the low bound of a range” -> “is the low bound of a range”?

8th bullet (for a named_container_aggregate) – “is of the form with” – I don’t
find this terribly clear, though maybe just changing “with” to “containing” 
would suffice.

15th (for a container_element_association) and final (the 
iterated_element_association) bullets – both of these have two “and”s – maybe 
the first “and” should go, and/or possibly the second “and” should be a 
“then”??

Examples

Various places seem to be missing a new-line and bolding, e.g. “type Set_Type
is private”, the various “S := ...”s.

****************************************************************

Questions? Ask the ACAA Technical Agent