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

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

!standard 4.3.5(0)          18-03-01 AI12-0212-1/05
!class Amendment 16-12-27
!status work item 17-06-10
!status received 16-06-12
!priority Low
!difficulty Hard
!subject Container aggregates
!summary
Add aggregate syntax to construct container instances.
!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. 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_Positional" 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_Positional => 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_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
!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}.
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_Positional => /procedure_/name][, Add_Named => /procedure_/name][, New_Indexed => /function_/name, Assign_Indexed => /procedure_/name])
A /procedure_/name shall be specified for at least one of Add_Positional, Add_Named, or Assign_Indexed. A /procedure_/name shall be specified for at most one of Add_Named or Assign_Indexed. 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_Positional, 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_Positional and either Add_Named or 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_expression_list => expression | iterated_element_association
key_expression_list ::= /key_/expression {| /key_/expression}
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 positional_container_aggregate shall have an Aggregate aspect that includes a specification for an Add_Positional procedure or an Assign_Indexed procedure. 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 or Assign_Indexed procedure. The expected type for each such /key_/expression is the key type of the expected type. [Redundant: An empty_container_aggregate may be used with any expected type that has an Aggregate aspect.]
Legality Rules
If the expected type T of an aggregate specifies an Assign_Indexed procedure in its Aggregate aspect, then the aggregate is called an /indexed aggregate/ if there is no Add_Positional procedure specified, or if the aggregate is a named_container_aggregate with a container_element_association that has a key_expression_list, or that has a loop_parameter_specification. The key type of an indexed aggregate is also called the /index type/ of the aggregate.
For an iterated_element_association without a /key_/expression, if the expected type T of the aggregate does not specify an Add_Positional procedure in its Aggregate aspect, or if the aggregate is an indexed aggregate, then the type of the loop parameter of the iterated_element_association shall be the same as the key type of T.
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. If there is no /key_/expression, the loop parameter itself is used as the key, and for Assign_Indexed, the iterated_element_association must be over a contiguous discrete range.
For a named_container_aggregate that is an indexed aggregate, all container_element_associations shall either have a key_expression_list or have a loop_parameter_specification. Furthermore, for such an aggregate, either:
* all /key_/expressions shall be static expressions, 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_/expressions 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_expression_list, the list shall have exactly one /key_/expression.
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 first and last index of the aggregate, and are determined as follows:
* if the aggregate is a positional_container_aggregate, the first index is the low bound of the subtype of the key parameter of the Add_Indexed procedure, and the last index has a position number that is the sum of the position number of the first index and one less than the number of expressions in the aggregate;
* if the aggregate is a named_container_aggregate, the first index is the lowest value appearing either within a key_expression_list or as the low bound of a range defined by a discrete_subtype_definition of a loop_parameter_specification; the last index is the highest vale appearing either within a key_expression_list or as 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_Positional procedure, each expression is evaluated in an arbitrary order, and the Add_Positional 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_expression_list, for each /key_/expression of the list in an arbitrary order, the /key_/expression is evaluated as is the expression of the container_element_association (in an arbitrary order), and the Add_Named procedure is invoked with the anonymous object A as the first parameter, the result of evaluating the /key_/expression 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;
* 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_Positional 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_Positional => 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_Positional => 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 (mostly, assuming set semantics) equivalent to S := (for Item in -5 .. 5 when Item /= 0 => Item);
-- A set aggregate with more than one item_selector (a TBD AI) S := (for X in 1 .. 10 => for Y in -1 .. 1 when Y /= 0 => X * Y);
-- is equivalent to S := Empty_Set; for X in 1 .. 10 loop for Y in -1 .. 1 loop if Y /= 0 then Include (S, X * Y); end if; end if; end if;
-- An example that combines all aspects of the grammar S := (for Customer of Customer_Database when not Customer.Poor => Customer.Name, for Manager of Managers => for Staff of Manager.Minions when Staff.Performance >= 3.0 => Staff.First_Name & Staff.Last_Name);
-- Is equivalent to S := Empty_Set; for Customer of Customer_Database loop if not Customer.Poor => Include (S, Customer.Name); end if; end loop; for Manager of Managers loop for Staff of Manager.Minions loop if Staff.Performance => 3.0 then Include (S, Staff.First_Name & Staff.Last_Name); end if; end loop; end loop;
-- 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_Positional => 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_Positional => 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_Positional => 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_Positional => Include);
!discussion
See !proposal for most of the discussion. Here we discuss some rejected alternatives or additions.
We plan to generalize array aggregates as well to allow arbitrary iterators, probably in a separate AI, or perhaps a subsequent revision of this one. 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.

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

Questions? Ask the ACAA Technical Agent