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

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

!standard 4.3.5(0)          18-01-24 AI12-0212-1/02
!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 new aspect (in the spirit of Constant_Indexing, Default_Iterator, and Iterator_Element) along with two new aggregates to use it.
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 syntax is highly inspired by Python (please do not let that put you off), it just is more Ada-like than comprehension in, say, Haskell.
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:
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 things you specify in the Aggregate aspect, the procedure "Include" is the other.
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. Thy 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. Two approaches are supported. This is the nested (or "product") one:
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:
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 (TBD -- not in this AI yet):
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 contract on 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");
So, to summarise: * New aspect on types specifying an empty default, plus either an
Add_Positional or an Add_Named procedure (or both).
* Extend aggregate syntax to trigger these
Related changes:
This AI will have some interesting interplay with declare expressions and generators.
!wording
4.3 Aggregates (Syntax)
Change paragraph 2/5:
aggregate ::= record_aggregate | extension_aggregate | array_aggregate | delta_aggregate
To:
aggregate ::= record_aggregate | extension_aggregate | array_aggregate | delta_aggregate | container_aggregate
Modify paragraph 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 of the form:
(Empty => name[, Add_Positional => /procedure_/name][, Add_Named => /procedure_/name])
Either Add_Positional, Add_Named, or both 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 have 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 /procedure_/name specified for Add_Named, if any, shall have three parameters, the first an IN OUT parameter of type T, the second an IN parameter of a nonlimited type, that is called the /key type/ of the container, and the third, an IN parameter of a nonlimited type that is called the /element type/ of T.
If both Add_Positional and Add_Named are specified, the final parameters shall be of the same type -- the element type of T.
Syntax
container_aggregate ::= positional_container_aggregate | named_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. 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. The expected type for each such /key_/expression is the key type of the expected type.
Legality Rules
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, 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.
Dynamic Semantics
The evaluation of a container_aggregate starts by creating an anonymous object A of the expected type T initialized 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, the parameter, if any, is initialized as follows:
* for a positional_container_aggregate, the number of expressions;
* for a named_container_aggregate without an iterated_element_association, the number of container_element_associations;
* otherwise, to an implementation-defined value.
The evaluation then proceeds as follows:
* for a positional_container_aggregate, each expression is evaluated in any 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 in the order of the expressions;
* 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:
* for a container_element_association with a key_expression_list, for each /key_/expression of the list in any order, the /key_/expression is evaluated as is the expression of the container_element_association (in any 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 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:
* 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
I KNOW I NEED TO REWORK THESE TO USE EXISTING THINGS IN THE RM
-- Declares a container type. type Set_Type is private
with Aggregate => (Empty => Empty_Set;
Add_Positional => Merge);
function Empty_Set return Set_Type;
procedure Merge (S : in out Set_Type; N : Integer) with Pre => N in 0 .. 1000;
private
type Set_Type is array (0 .. 1000) of Boolean
function Empty_Set return Set_Type is (others => False);
-- A positional set aggregate S := (1, 2);
-- is Equivalent to: S := Empty_Set; S.Include (1); S.Include (2);
-- A named map aggregate M := (12 => "house", 14 => "beige");
-- 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 S.Include (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 S.Include (Item); end if; end loop;
-- A set aggregate consisting out 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 S.Include (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 => S.Include (Customer.Name); end if; end loop; for Manager of Managers loop for Staff of Manager.Minions loop if Staff.Performance => 3.0 then S.Include (Staff.First_Name & Staff.Last_Name); end if; end loop; 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);
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
!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, 2017  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).

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


Questions? Ask the ACAA Technical Agent