!standard 4.3.5(0) 19-01-27 AI12-0212-1/10 !standard 1.1.4(12) !standard 1.1.4(13) !standard 2.1(15) !standard 2.2(9/5) !standard 3.7.1(3) !standard 3.8.1(4) !standard 4.3(2/5) !standard 4.3(3/5) !standard 4.3.1(5) !standard 4.3.3(3/2) !standard 4.3.3(4/5) !standard 4.3.3(5.1/5) !standard 4.3.3(9) !standard 4.3.3(17/5) !standard 4.3.3(21) !standard 4.3.3(23.2/5) !standard 4.3.3(26) !standard 4.3.3(27) !standard 4.3.3(31) !standard 4.3.4(4/5) !standard 4.4(3.1/3) !standard 11.2(3) !standard 13.1.1(5/3) !standard 13.1.1(7/3) !class Amendment 16-12-27 !status Amendment 1-2012 18-11-19 !status ARG Approved 5-0-2 18-10-21 !status work item 17-06-10 !status received 16-06-12 !priority Low !difficulty Hard !subject Container aggregates; generalized array aggregates !summary Add aggregate syntax to construct container instances, using a syntax based on array aggregates, but using square brackets ("[...]"). Allow container iterators in array aggregates, and permit the use of square brackets, which eases the specification of a null array and a single-element array, without resorting to somewhat awkward named notation. !problem Currently, there are very few constructors for containers. We have for example in the Set container package, 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 postcondition) - can make code harder to read since construction has to be moved to a separate function One approach would be to add functions that initialise a container from an aggregate of an unconstrained array, for example: X : Example.Set := Example.From_Array ((1, 2, 3)); However there are a number of drawbacks: the syntax is ugly (double brackets), it would not allow comprehensions (i.e. containers defined by a nested iterator), only displays (i.e. an explicit list of components), and it would only work for array-like containers and not maps. There are three places where container aggregates may be useful: in ordinary code (but here we can just do what we do right now), for the definition of constants, and in contracts. !proposal Thus, we propose to add a new aspect (in the spirit of Constant_Indexing, Default_Iterator, and Iterator_Element) along with a new kind of aggregate to use it, inspired by the array aggregate syntax. This proposal works for most of the standard containers (Vectors, Doubly_Linked_Lists, Hashed_Sets, Ordered_Sets, Hashed_Maps and Ordered_Maps including their bounded and indefinite versions). Multiway_Trees are not supported by this proposal. The concept is inspired in part by Python and other languages that have list and set "comprehensions," but it uses, largely, Ada's array aggregate as the basis for the syntax. One challenge with existing array aggregates is that an empty array aggregate (a "null array" in Ada parlance) requires the use of a null range. When the container is not indexed by contiguous discrete key values, this poses a bit of a problem. Single-element array aggregates are also awkward in Ada, in that positional notation cannot be used. Therefore, we propose to adopt the use of square brackets ("[...]") for container aggregates. We also propose (though it is not essential to the container aggregate proposal and could be carved out) to permit the use of square brackets for array aggregates, which would allow the use of "[]" as an empty array aggregate, with semantics like that of empty string literals. We do not propose to permit the use of square brackets for record or extension aggregates, as we believe arrays and containers are similar, in that they represent a data structure made of elements all of the same type, while record aggregates are inherently a different beast, and already have their own special "(null record)" syntax for the rare empty record aggregate. Furthermore, by disallowing record-ish aggregates from using square brackets, we can allow container aggregates for visible record or record extensions, rather than restricting them to private types. Finally, we already somewhat equated arrays and containers in the "for X of Container_Or_Array ..." iterator syntax, while there is (of course!) no corresponding iteration defined over a record type. For delta aggregates, square brackets would be permitted for array delta aggregates, but not for record delta aggregates. At some point, we could also imagine permitting the use of square brackets for array (and container) indexing, but we see that as a separate AI. In the most simple form (called a positional container aggregate) we just give the elements to add in a form very similar to a simple array aggregate, but with no "others" permitted: X : My_Set := [1, 2, 3]; -- Equivalent to: X : My_Set := Empty_Set; Include (X, 1); Include (X, 2); Include (X, 3); The function or constant "Empty_Set" is one of the two (or three) things you specify in the Aggregate aspect (the "Empty" component of the Aggregate aspect), the procedure "Include" is the other (the "Add_Unnamed" component of the Aggregate aspect). Positional container aggregates are good for defining simple container instances, but often, you might want to derive one container from another (a concept that is called comprehension). This is vaguely related to a delta aggregate, but neither construct can easily emulate the other. Here we loop over one container and apply some function to each item. The proposed syntax steals from existing loop and quantifier syntax: Y : My_Set := [for Item of X => Item * 2]; -- Equivalent to: Y : My_Set := Empty_Set; for Item of X loop Include (Y, Item * 2); end loop; We may also wish to filter the items (not in this AI, see AI12-0250-1): 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]; -- Not proposed now (not 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]; -- This *is* part of this AI -- 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)]; -- Not proposed now (not part of this AI) -- 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 -- Ada 2012 have these: Constant_Indexing => Constant_Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, -- This is new Aggregate => (Empty => Empty_Set, Add_Unnamed => Include); For containers such as maps, an Add_Named component of the Aggregate aspect would be specified, to enable a usage such as: M : My_Map := [42 => "foo", 88 => "bar"]; This is equivalent to: M := Empty_Map; Insert (M, 42, "foo"); Insert (M, 88, "bar"); where "Insert" is specified as the "Add_Named" component of the Aggregate aspect. To accommodate array-like containers that require a contiguous range of "key" values, and might not be extensible, we allow the specification of a New_Indexed function that creates an indexed structure with the appropriate bounds, and an Assign_Indexed procedure that assigns a value into a particular indexed element. Aggregates that use these operations are called "indexed aggregates" and have many of the same rules as array aggregates with respect to uniqueness of indices and full coverage. So, to summarize: * New aspect on types specifying an empty default, plus an Add_Named procedure, or an Add_Unnamed procedure and/or an Assign_Indexed procedure (with a New_Indexed function to go with it). * Extend aggregate syntax to support these based on the expected type * Use square brackets for container aggregates (to solve the empty aggregate problem and simplify the singleton aggregate); also permit the square bracket syntax in array aggregates, and array delta aggregates. We also provide wording to allow container iterators within an array aggregate. !wording ---- Wording for allowing square brackets as a syntax element: Modify 1.1.4(12): * A vertical line separates alternative items[ unless it occurs immediately after an opening curly bracket, in which case it stands for itself]: Delete the second example (discrete_choice_list) in 1.1.4(13). Add after 1.1.4(13): * For symbols used in this notation (square brackets, curly brackets, and the vertical line), the symbols when surrounded by ‘ represent themselves: discrete_choice_list ::= discrete_choice {'|' discrete_choice} named_container_aggregate ::= '[' container_element_association_list ']' Add [ ] to the list of character names in 2.1(15) and list of delimiters in 2.2(9/5). Replace "{|" with "{'|'" in 3.7.1(3), 3.8.1(4), 4.3.1(5), 4.4(3.1/3), and 11.2(3). ---- Wording to allow an aggregate as the entity denoted by an aspect: 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; ---- Wording to add container aggregates as a type of aggregate: 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] {an array_aggregate, a record_aggregate, or an extension_aggregate} shall be a single array type, record type, or record extension. {The expected type for a container_aggregate shall be a single array type or a single type with the Aggregate aspect specified.} ---- wording for adding container iterators to array aggregates ---- and allowing the use of "[...]" Replace 4.3.3(3/2) with: positional_array_aggregate ::= (expression, expression {, expression}) | (expression {, expression}, others => expression) | (expression {, expression}, others => <>) | '[' expression{, expression}[, others => expression] ']' | '[' expression{, expression}, others => <> ']' | '[' ']' Replace 4.3.3(4/5) with: named_array_aggregate ::= (array_component_association_list) | '['array_component_association_list']' Replace 4.3.3(5.1/5): iterated_component_association ::= for defining_identifier in discrete_choice_list => expression with: iterated_component_association ::= for defining_identifier in discrete_choice_list => expression | for iterator_specification => expression Modify 4.3.3(9): An array_aggregate of an n-dimensional array type shall be written as an n-dimensional array_aggregate{, or as a /null aggregate/ -- [ ]}. Add after 4.3.3(17/5): Either all or none of the array_component_associations of an array_component_association_list shall be iterated_component_associations with an iterator_specification. Add before 4.3.3(21): For an array_aggregate that contains only array_component_associations that are iterated_component_associations with iterator_specifications, evaluation proceeds in two steps: 1. Each iterator_specification is elaborated (in an arbitrary order) and an iteration is performed solely to determine a count of the number of values produced by the iteration; all of these counts are combined to determine the overall length of the array, and ultimately the bounds of the array (defined below); 2. A second iteration is performed for each of the iterator_specifications, in the order given in the aggregate, and for each value produced, the associated expression is evaluated, its value is converted to the component subtype of the array type, and used to define the value of the next component of the array starting at the low bound and proceeding sequentially toward the high bound. A check is made that the second iteration results in the same array length; Constraint_Error is raised if this check fails. AARM To Be Honest: Constraint_Error should be raised no later than when the iterations exceed the expected array length; memory that doesn't belong to the aggregate temporary should not be overwritten. Modify 4.3.3(21) as follows: The evaluation of [an] {any other} array_aggregate of a given array type proceeds in two steps: Modify 4.3.3(23.2/5) During an evaluation of the expression of an iterated_component_association {with a discrete_choice_list}, the value of the corresponding index parameter is that of the corresponding index of the corresponding array component. {During an evaluation of the expression of an iterated_component_association with an iterator_specification, the value of the loop parameter of the iterator_specification is the value produced by the iteration (as described in 5.5.2).} Modify 4.3.3(26): For a positional_array_aggregate Redundant[(or equivalent string_literal)] without an /others/ choice{ that is not a null aggregate}, the lower bound is that of the corresponding index range in the applicable index constraint, if defined, or that of the corresponding index subtype, if not; in either case, the upper bound is determined from the lower bound and the number of expressions Redundant[(or the length of the string_literal)]; {For a null aggregate, bounds for each dimension are determined as for a positional_array_aggregate with zero expressions for each dimension;} {AARM Reason: We need a separate rule to describe what happens for a multidimensional null aggregate; the single dimension case directly follows from the positional_array_aggregate definition.} {For a named_array_aggregate containing only iterated_component_associations with an iterator_specification, the lower bound is determined as for a positional_array_aggregate without an /others/ choice, and the upper bound is determined from the lower bound and the total number of values produced by the iteration(s); Modify 4.3.3(27): For {any other}[a] named_array_aggregate without an others choice, the bounds are determined by the smallest and largest index values covered by any discrete_choice_list. Add after 4.3.3(31): Implementation Permissions When evaluating iterated_component_associations for an array_aggregate that contains only iterated_component_associations with iterator_specifications, the first step of evaluating a iterated_component_association can be omitted if the implementation can determine the number of values by some other means. AARM Discussion: For instance, if the type of the aggregate is constrained, the implementation can (but does not have to) calculate the expected length from the constraint. Replace 4.3.4(4/5) with: array_delta_aggregate ::= (base_expression with delta array_component_association_list) | '[' base_expression with delta array_component_association_list ']' ---- end of wording for array [delta] aggregate enhancement ---- ---- Add new section, 4.3.5, for container aggregates: 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. For a type other than an array type, the following type-related operational aspect may be specified: Aggregate This aspect is an aggregate of the form: (Empty => name[, Add_Named => /procedure_/name][, Add_Unnamed => /procedure_/name][, New_Indexed => /function_/name, Assign_Indexed => /procedure_/name]) The type for which this aspect is specified is known as the /container type/ of the Aggregate aspect. A /procedure_/name shall be specified for at least one of Add_Named, Add_Unnamed, or Assign_Indexed. If Add_Named is specified, neither Add_Unnamed nor Assign_Indexed shall be specified. Either both or neither of New_Indexed and Assign_Indexed shall be specified. Name Resolution Rules The name specified for Empty for an Aggregate aspect shall denote a constant of the container type, or denote a function with a result type of the container type that has no parameters, or that has one IN parameter of type Integer. AARM Reason: In the function case, the parameter, if present, may be used to specify an initial size for the container, in anticipation of adding elements to it. For a positional aggregate, or a named aggregate that doesn't use an iterator, it will be initialized with the number of elements. For a named aggregate that uses an iterator, the implementation is permitted to estimate the number of elements that the iterator will produce, but it is not required to do so. The /procedure_/name specified for Add_Unnamed for an Aggregate aspect, shall denote a procedure that has two parameters, the first an IN OUT parameter of the container type, and the second an IN parameter of some nonlimited type, called the /element type/ of the container type. The /function_/name specified for New_Indexed for an Aggregate aspect shall denote a function with a result type of the container type, and two parameters of the same discrete type, with that type being the /key type/ of the container type. 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 for an Aggregate aspect shall denote a procedure that has three parameters, the first an IN OUT parameter of the container type, the second an IN parameter of a nonlimited type (the /key type/ of the container), and the third, an IN parameter of a nonlimited type that is called the /element type/ of the container type. Legality Rules If the container type of an Aggregate aspect is a private type, the full type of the container type shall not be an array type. If container type is limited, the name specified for Empty shall denote a function rather than a constant object. For an Aggregate aspect, the key type of Assign_Indexed shall be the same type as that of the parameters of New_Indexed. Additionally, if both Add_Unnamed and Assign_Indexed are specified, the final parameters shall be of the same type -- the element type of the container type. Static Semantics The Aggregate aspect is nonoverridable. Syntax container_aggregate ::= null_container_aggregate | positional_container_aggregate | named_container_aggregate null_container_aggregate ::= '[' ']' positional_container_aggregate ::= '[' expression{, expression} ']' named_container_aggregate ::= '[' container_element_association_list ']' container_element_association_list ::= container_element_association {, container_element_association} container_element_association ::= key_choice_list => expression | key_choice_list => <> | iterated_element_association key_choice_list ::= key_choice {'|' key_choice} key_choice ::= /key_/expression | discrete_range iterated_element_association ::= for loop_parameter_specification[ use /key_/expression] => expression | for iterator_specification[ use /key_/expression] => expression Name Resolution Rules The expected type for a container_aggregate shall be a type for which the Aggregate aspect has been specified. The expected type for each expression of a container_aggregate is the element type of the expected type. The expected type for a /key_/expression, or a discrete_range of a key_choice, is the key type of the expected type of the aggregate. Legality Rules The expected type for a positional_container_aggregate shall have an Aggregate aspect that includes a specification for an Add_Unnamed procedure or an Assign_Indexed procedure. The expected type for a named_container_aggregate that contains one or more iterated_element_associations with a /key_/expression shall have an Aggregate aspect that includes a specification for the Add_Named procedure. The expected type for a named_container_aggregate that contains one or more key_choice_lists shall have an Aggregate aspect that includes a specification for the Add_Named or Assign_Indexed procedure. [Redundant: A null_container_aggregate can be of any type with an Aggregate aspect.] A non-null container aggregate is called an /indexed aggregate/ if the expected type T of the aggregate specifies an Assign_Indexed procedure in its Aggregate aspect, and either there is no Add_Unnamed procedure specified for the type, or the aggregate is a named_container_aggregate with a container_element_association that contains a key_choice_list or a loop_parameter_specification. The key type of an indexed aggregate is also called the /index type/ of the aggregate. A container_element_association with a <> rather than an expression, or with a key_choice that is a discrete_range, is permitted only in an indexed aggregate. AARM Reason: Only an element of an indexed aggregate can be left uninitialized, because the compiler can simply omit producing an Assign_Indexed operation for the given index. For other kinds of aggregates, there are no operations that add an element without giving it a value. Note that to be consistent with array aggregates, we do not support specifying <> in an iterated_element_association. For an iterated_element_association without a /key_/expression, if the aggregate is an indexed aggregate or the expected type of the aggregate specifies an Add_Named procedure in its Aggregate aspect, then the type of the loop parameter of the iterated_element_association shall be the same as the key type of the aggregate. AARM Ramification: If there is a /key_/expression in an iterated_element_association, it determines the key of each added key/value pair, rather than the loop parameter. But if there is no /key_/expression, the loop parameter itself is used as the key. For a named_container_aggregate that is an indexed aggregate, all container_element_associations shall contain either a key_choice_list, or a loop_parameter_specification without a /key_/expression. Furthermore, for such an aggregate, either: * all key_choices shall be static expressions or static ranges, and every loop_parameter_specification shall have a discrete_subtype_definition that defines a non-null static range, and the set of values of the index type covered by the key_choices and the discrete_subtype_definitions shall form a contiguous range of values with no duplications; or * there shall be exactly one container_element_association, and if it has a key_choice_list, the list shall have exactly one key_choice. AARM Reason: The above is trying to mimic the rules for named array aggregates, without others. Dynamic Semantics The evaluation of a container_aggregate starts by creating an anonymous object A of the expected type T initialized as follows: * if the aggregate is an indexed aggregate, from the result of a call on the New_Indexed function; the actual parameters in this call represent the lower and upper bound of the aggregate, and are determined as follows: * if the aggregate is a positional_container_aggregate, the lower bound is the low bound of the subtype of the key parameter of the Add_Indexed procedure, and the upper bound has a position number that is the sum of the position number of the lower bound and one less than the number of expressions in the aggregate; * if the aggregate is a named_container_aggregate, the lower bound is the lowest value covered by a key_choice_list or is the low bound of a range defined by a discrete_subtype_definition of a loop_parameter_specification; the upper bound is the highest value covered by a key_choice_list or is the high bound of a range defined by a discrete_subtype_definition of a loop_parameter_specification. * 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 a null_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 contains a loop_parameter_specification, the total number of elements specified by all of the container_element_associations; * otherwise, to an implementation-defined value. AARM Implementation Note: This value ought to be an estimate for the number of elements in the aggregate, if one is available. If not, it is suggested to use the default value for the parameter if one exists, and zero otherwise. The evaluation then proceeds as follows: * for a null_container_aggregate, the anonymous object A is the result; * for a positional_container_aggregate of a type with a specified Add_Unnamed procedure, each expression is evaluated in an arbitrary order, and the Add_Unnamed procedure is invoked in sequence with the anonymous object A as the first parameter and the result of evaluating each expression as the second parameter, in the order of the expressions; * for a positional_container_aggregate that is an indexed aggregate, each expression is evaluated in an arbitrary order, and the Assign_Indexed procedure is invoked in sequence with the anonymous object A as the first parameter, the key value as the second parameter, computed by starting with the low bound of the subtype of the key formal parameter of the Assign_Indexed procedure and taking the successor of this value for each successive expression, and the result of evaluating each expression as the third parameter; * for a named_container_aggregate for a type with an Add_Named procedure in its Aggregate aspect, the container_element_associations are evaluated in an arbitrary order: * for a container_element_association with a key_choice_list, for each key_choice of the list in an arbitrary order, the key_choice is evaluated as is the expression of the container_element_association (in an arbitrary order), and the Add_Named procedure is invoked once for each value covered by the key_choice, with the anonymous object A as the first parameter, the value from the key_choice as the second parameter, and the result of evaluating the expression as the third parameter; * for a container_element_association with an iterated_element_association, the iterated_element_association is elaborated, then an iteration is performed as described in 5.5 or 5.5.2, and for each value of the loop parameter of the iteration the Add_Named procedure is invoked with the anonymous object A as the first parameter, the result of evaluating the expression as the third parameter, and: * if there is a /key_/expression, the result of evaluating the /key_/expression as the second parameter; * otherwise, with the loop parameter as the second parameter; * for a named_container_aggregate that is an indexed aggregate, the evaluation proceeds as above for the case of Add_Named, but with the Assign_Indexed procedure being invoked in its stead; in the case of a container_element_association with a <> rather than an expression, the corresponding call on Assign_Indexed is not performed, leaving the component as it was upon return from the New_Indexed function; * for any other named_container_aggregate, the container_element_associations (which are necessarily iterated_element_associations) are evaluated in the order given; each such evaluation comprises two steps: 1. the iterated_element_association is elaborated; 2. an iteration is performed as described in 5.5 or 5.5.2, and for each value of the loop parameter of the iteration, the Add_Unnamed procedure is invoked, with the anonymous object A as the first parameter and the result of evaluating the expression as the second parameter. AARM Ramification: In this case, the value of the loop parameter is not directly relevant, though presumably it appears within the expression of the iterated_element_association. Examples Declarations of Set_Type, Map_Type, and Vector_Type: -- Set_Type is a set-like container type. type Set_Type is private with Aggregate => (Empty => Empty_Set, Add_Unnamed => Include); function Empty_Set return Set_Type; subtype Small_Natural is Natural range 0..1000; procedure Include (S : in out Set_Type; N : Small_Natural); -- Map_Type is a map-like container type. type Map_Type is private with Aggregate => (Empty => Empty_Map, Add_Named => Add_To_Map); procedure Add_To_Map (M : in out Map_Type; Key : Integer; Value : String); Empty_Map : constant Map_Type; -- Vector_Type is an extensible array-like container type. type Vector_Type is private with Aggregate => (Empty => Empty_Vector, Add_Unnamed => Append_One, New_Indexed => New_Vector, Assign_Indexed => Assign_Element) function Empty_Vector (Capacity : Count_Type := 0) return Vector_Type; procedure Append_One (V : in out Vector_Type; New_Item : String); procedure Assign_Element (V : in out Vector_Type; Index : Positive; Item : String); function New_Vector (First, Last : Positive) return Vector_Type with Pre => First = Positive'First; -- Vectors of are always indexed starting at the -- low bound of their index subtype. private type Set_Type is new Bit_Vector (Small_Natural); -- see 3.6 function Empty_Set return Set_Type is (others => False); package Int_String_Maps is new Ada.Containers.Indefinite_Ordered_Maps -- see A.18.14 (Key_Type => Integer, Element_Type => String); type Map_Type is new Int_String_Maps.Map with null record; procedure Add_To_Map (M : in out Map_Type; Key : Integer; Value : String) renames Insert; Empty_Map : constant Map_Type := (Int_String_Maps.Empty_Map with null record); package String_Vectors is new Ada.Containers.Indefinite_Vectors -- see A.18. (Index_Type => Positive, Element_Type => String); type Vector_Type is new String_Vectors.Vector; Examples of container aggregates for Set_Type, Map_Type, and Vector_Type -- Example aggregates using Set_Type S : Set_Type; -- Assign S to be the 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 consisting of two iterated_element_associations S := [for Item in 1 .. 5 => Item, for Item in 1 .. 5 => -Item]; -- Is equivalent (assuming set semantics) to S := Empty_Set; for Item in -5 .. 5 loop if Item /= 0 then Include (S, Item * 2); end if; 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 use 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 use 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 (); for Elem of M loop Add_Positional (V, Elem); end loop; A.18.2 The Generic Package Containers.Vectors Add the Aggregate aspect to the existing ones on type Vector: type Vector is tagged private with Constant_Indexing => Constant_Reference, Variable_Indexing => Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_Vector, Add_Unnamed => Append_One, New_Indexed => New_Vector, Assign_Indexed => Replace_Element); -- Need to add these subprograms: function New_Vector (First, Last : Index_Type) return Vector is (To_Vector (Last)) with Pre => First = Index_Type'First; procedure Append_One (Container : in out Vector; New_Item : in Element_Type); A.18.3 The Generic Package Containers.Doubly_Linked_Lists Add the Aggregate aspect to the existing ones on type List: type List is tagged private with Constant_Indexing => Constant_Reference, Variable_Indexing => Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_List, Add_Unnamed => Append); A.18.5 The Generic Package Containers.Hashed_Maps Add the Aggregate aspect to the existing ones on type Map: type Map is tagged private with Constant_Indexing => Constant_Reference, Variable_Indexing => Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_Map, Add_Named => Insert); A.18.6 The Generic Package Containers.Ordered_Maps Add the Aggregate aspect to the existing ones on type Map: type Map is tagged private with Constant_Indexing => Constant_Reference, Variable_Indexing => Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_Map, Add_Named => Insert); A.18.8 The Generic Package Containers.Hashed_Sets Add the Aggregate aspect to the existing ones on type Set: type Set is tagged private with Constant_Indexing => Constant_Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_Set, Add_Unnamed => Include); A.18.9 The Generic Package Containers.Ordered_Sets Add the Aggregate aspect to the existing ones on type Set: type Set is tagged private with Constant_Indexing => Constant_Reference, Default_Iterator => Iterate, Iterator_Element => Element_Type, Aggregate => (Empty => Empty_Set, Add_Unnamed => Include); !discussion See !proposal for most of the discussion. Here we discuss some rejected alternatives or additions. We originally used "Add_Positional" instead of "Add_Unnamed" in the Aggregate aspect, but that created confusion because the Add_Unnamed operation is used for both named and positional aggregates. Add_Unnamed is used for named aggregates when they use a container iterator without a specified key-expression. Before we had Assign_Indexed, we allowed both Add_Unnamed and Add_Named for a single type. But in fact, it really doesn't make much sense to allow both, since what would be the "default" value of the key if you were to apply Add_Unnamed to something like a "map" container. On the other hand, both Add_Unnamed and Assign_Indexed can be specified for the same (vector-like) type, since the container maintains a high bound for its index type, so Add_Unnamed can by default extend the vector and assign the new last element. We include above a generalization of array aggregates to allow container iterators. Originally we were concerned about making this generalization, because to determine the size of the array to create, you will need to execute the iterator until it ends. We initially presumed you would have to save all of the values somewhere, create the array, and finally initialize the array components. However, by just getting cursors but not the actual iterator elements, you can do the iteration efficiently and determine an upper bound for the size of the array. We chose "use" 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 use 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 We also considered a comma, a vertical bar, one or two colons, "do," "with," etc. None of the choices were ideal, but we note that when it is combined with a filtered iterator (AI12-0250-1), "use" works pretty well after the "when" clause because it is a verb: M := [for P of Table when Is_Good(P) use P.Key => P.Value]; Also, "for ... use" is a familiar pairing of reserved words in Ada for specifying attributes, so it is not an unprecedented combination. Our second choice is probably comma, because it is quite light, and works adequately with or without a "when" clause, but it was pointed out that we allow multiple iterated_element_associations, which are themselves separated by commas. !corrigendum 1.1.4(12) @drepl @xbullet @dby @xbullet !corrigendum 1.1.4(13) @drepl @xindent<@fa@ ::=@ @fa@ |@ @fa@hr @fa@ ::=@ @fa@ {|@ @fa}> @dby @xindent<@fa@ ::=@ @fa@ |@ @fa> @xbullet @xindent<@fa@ ::=@ @fa@ {'|'@ @fa}@hr @fa@ ::=@ '['@ @fa@ ']'> !comment Add [ and ] to the table of characters in 2.1(15). Not doing that here. !corrigendum 2.2(9) @drepl & @ @ ' @ @ ( @ @ ) @ @ * @ @ + @ @ , @ @ @endash @ @ . @ @ / @ @ : @ @ ; @ @ < @ @ = @ @ @> @ @ | @dby & @ @ ' @ @ ( @ @ ) @ @ * @ @ + @ @ , @ @ @endash @ @ . @ @ / @ @ : @ @ ; @ @ < @ @ = @ @ @> @ @ @@ @ @ [ @ @ ] @ @ | !corrigendum 3.8.1(4) @drepl @xindent<@fa@fa<@ ::=@ >@fa@ {|@ @fa}> @dby @xindent<@fa@fa<@ ::=@ >@fa@ {'|'@ @fa}> !comment Replace "{|" with "{'|'" in 3.7.1(3), 4.3.1(5), and 11.2(3). !comment Not making these changes here. !corrigendum 4.3(2/5) @drepl @xindent<@fa@fa<@ ::=@ >@fa@ |@ @fa@ |@ @fa> @dby @xindent<@fa@fa<@ ::=@ > @ @ @ @ @fa@ |@ @fa@ |@ @fa@hr @ @ |@ @fa@ |@ @fa> !corrigendum 4.3(3/5) @drepl The expected type for a @fa 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 @fa shall be a single array type, record type, or record extension. @dby The expected type for a @fa shall be a single array type, or a single descendant of a record type or of a record extension. The expected type for an @fa, a @fa, or an @fa shall be a single array type, record type, or record extension. The expected type for a @fa shall be a single array type or a single type with the Aggregate aspect specified. !corrigendum 4.3.3(3/2) @drepl @xindent<@fa@fa<@ ::=@ >@hr @ @ @ @ (@fa,@ @fa@ {,@ @fa})@hr @ @ |@ (@fa@ {,@ @fa},@ @b@ =@>@ @fa)@hr @ @ |@ (@fa@ {,@ @fa},@ @b@ =@>@ <@>> @dby @xindent<@fa@fa<@ ::=@ >@hr @ @ @ @ (@fa,@ @fa@ {,@ @fa})@hr @ @ |@ (@fa@ {,@ @fa},@ @b@ =@>@ @fa)@hr @ @ |@ (@fa@ {,@ @fa},@ @b@ =@>@ <@>)@hr @ @ |@ '['@ @fa@ {,@ @fa}[,@ @b@ =@>@ @fa]@ ']'@hr @ @ |@ '['@ @fa@ {,@ @fa},@ @b@ =@>@ <@>@ ']'@hr @ @ |@ '['@ ']'> !corrigendum 4.3.3(4) @drepl @xindent<@fa@fa<@ ::=@ >@hr @ @ @ @ (@fa@ {,@ @fa})> @dby @xindent<@fa@fa<@ ::=@ >@hr @ @ @ @ (@fa)@hr @ @ |@ '['@ @fa@ ']'> @xindent<@fa@fa<@ ::=@ >@hr @ @ @ @ @fa@ {,@ @fa}> !corrigendum 4.3.3(5/5) @dinsa @xindent<@fa@fa<@ ::=@ >@hr @ @ @ @ @fa@ =@>@ @fa@hr @ @ |@ @fa =@> <@>@hr @ @ |@ @fa> @dinst @xindent<@fa@fa<@ ::=@ >@hr @ @ @ @ @b @fa@ @b@ @fa@ =@>@ @fa@hr @ @ |@ @b @fa@ =@>@ @fa> !corrigendum 4.3.3(9) @drepl An @fa of an n-dimensional array type shall be written as an n-dimensional @fa. @dby An @fa of an n-dimensional array type shall be written as an n-dimensional @fa, or as a @i @emdash [ ]. !corrigendum 4.3.3(17/5) @dinsa The @fa of an @fa (including an @fa) is allowed to have a @fa that is a nonstatic @fa or that is a @fa or @fa that defines a nonstatic or null range, only if it is the single @fa of its @fa, and either there is only one @fa in the enclosing @fa or the enclosing @fa is an @fa, not an @fa. @dinst Either all or none of the @fas of an @fa shall be @fas with an @fa. !corrigendum 4.3.3(21) @drepl The evaluation of an @fa of a given array type proceeds in two steps: @dby For an @fa that contains only @fas that are @fas with @fas, evaluation proceeds in two steps: @xhang<@xterms<1.>Each @fa is elaborated (in an arbitrary order) and an iteration is performed solely to determine a count of the number of values produced by the iteration; all of these counts are combined to determine the overall length of the array, and ultimately the bounds of the array (defined below);> @xhang<@xterms<2.>A second iteration is performed for each of the @fas, in the order given in the @fa, and for each value produced, the associated @fa is evaluated, its value is converted to the component subtype of the array type, and used to define the value of the next component of the array starting at the low bound and proceeding sequentially toward the high bound. A check is made that the second iteration results in the same array length; Constraint_Error is raised if this check fails.> The evaluation of any other @fa of a given array type proceeds in two steps: !corrigendum 4.3.3(23.1/4) @dinsa Each @fa in an @fa defines the value for the associated component(s). For an @fa with <@>, the associated component(s) are initialized to the Default_Component_Value of the array type if this aspect has been specified for the array type; otherwise, they are initialized by default as for a stand-alone object of the component subtype (see 3.3.1). @dinst During an evaluation of the @fa of an @fa with a @fa, the value of the corresponding index parameter is that of the corresponding index of the corresponding array component. During an evaluation of the @fa of an @fa with an @fa, the value of the loop parameter of the @fa is the value produced by the iteration (as described in 5.5.2). !corrigendum 4.3.3(26) @drepl @xbullet (or equivalent @fa) without an @b choice, the lower bound is that of the corresponding index range in the applicable index constraint, if defined, or that of the corresponding index subtype, if not; in either case, the upper bound is determined from the lower bound and the number of @fas (or the length of the @fa);> @dby @xbullet (or equivalent @fa) without an @b choice that is not a null aggregate, the lower bound is that of the corresponding index range in the applicable index constraint, if defined, or that of the corresponding index subtype, if not; in either case, the upper bound is determined from the lower bound and the number of @fas (or the length of the @fa);> @xbullet without an @b choice with zero expressions for each dimension;> @xbullet containing only @fas with an @fa, the lower bound is determined as for a @fa without an @b choice, and the upper bound is determined from the lower bound and the total number of values produced by the iteration(s);> !corrigendum 4.3.3(27) @drepl For a @fa without an @b choice, the bounds are determined by the smallest and largest index values covered by any @fa. @dby For any other @fa without an @b choice, the bounds are determined by the smallest and largest index values covered by any @fa. !corrigendum 4.3.3(31) @dinsa The exception Constraint_Error is raised if any of the above checks fail. @dinst @s8<@i> When evaluating @fas for an @fa that contains only @fas with @fas, the first step of evaluating a @fa can be omitted if the implementation can determine the number of values by some other means. !corrigendum 4.3.4(0) @dinsc A fake to force a conflict; the actual text is in the conflict file. !corrigendum 4.3.5(0) @dinsc In a @fa, values are specified for elements of a container; for a @fa, the elements are given sequentially; for a @fa, the elements are specified by a sequence of key/value pairs, or using an iterator. The Aggregate aspect of the type of the @fa determines how the elements are combined to form the container. For a type other than an array type, the following type-related operational aspect may be specified: @xhang<@xtermThis aspect is an @fa of the form:> @xindent<@ @ @ (Empty =@> @fa[,@hr @ @ @ @ Add_Named =@> @i@fa][,@hr @ @ @ @ Add_Unnamed =@> @i@fa][,@hr @ @ @ @ New_Indexed =@> @i@fa,@hr @ @ @ @ Assign_Indexed =@> @i@fa])> @xindent of the Aggregate aspect. A @i@fa shall be specified for at least one of Add_Named, Add_Unnamed, or Assign_Indexed. If Add_Named is specified, neither Add_Unnamed nor Assign_Indexed shall be specified. Either both or neither of New_Indexed and Assign_Indexed shall be specified.> @s8<@i> The @fa specified for Empty for an Aggregate aspect shall denote a constant of the container type, or denote a function with a result type of the container type that has no parameters, or that has one @b parameter of type Integer. The @i@fa specified for Add_Unnamed for an Aggregate aspect shall denote a procedure that has two parameters, the first an @b parameter of the container type, and the second an @b parameter of some nonlimited type, called the @i of the container type. The @i@fa specified for New_Indexed for an Aggregate aspect shall denote a function with a result type of the container type, and two parameters of the same discrete type, with that type being the @i of the container type. The @i@fa specified for Add_Named or Assign_Indexed for an Aggregate aspect shall denote a procedure that has three parameters, the first an @b parameter of the container type, the second an @b parameter of a nonlimited type (the @i of the container type), and the third, an @b parameter of a nonlimited type that is called the @i of the container type. @s8<@i> If the container type of an Aggregate aspect is a private type, the full type of the container type shall not be an array type. If container type is limited, the name specified for Empty shall denote a function rather than a constant object. For an Aggregate aspect, the key type of Assign_Indexed shall be the same type as that of the parameters of New_Indexed. Additionally, if both Add_Unnamed and Assign_Indexed are specified, the final parameters shall be of the same type @emdash the element type of the container type. @s8<@i> The Aggregate aspect is nonoverridable. @s8<@i> @xindent<@fa@fa<@ ::=@ >@hr @ @ @ @ @fa@hr @ @ |@ @fa@hr @ @ |@ @fa> @xindent<@fa@fa<@ ::=@ >'['@ ']'> @xindent<@fa@fa<@ ::=@ >'['@ @fa{,@ @fa}@ ']'> @xindent<@fa@fa<@ ::=@ >'['@ @fa@ ']'> @xindent<@fa@fa<@ ::=@ >@hr @ @ @ @ @fa@ {,@ @fa}> @xindent<@fa@fa<@ ::=@ >@hr @ @ @ @ @fa =@> @fa@hr @ @ |@ @fa =@> <@>@hr @ @ |@ @fa> @xindent<@fa@fa<@ ::=@ >@fa@ {'|'@ @fa}> @xindent<@fa@fa<@ ::=@ >@i@fa@ |@ @fa> @xindent<@fa@fa<@ ::=@ >@hr @ @ @ @ @b@ @fa[@ @b@ @i@fa]@ =@>@ @fa@hr @ @ |@ @b@ @fa[@ @b@ @i@fa]@ =@>@ @fa> @s8<@i> The expected type for a @fa shall be a type for which the Aggregate aspect has been specified. The expected type for each @fa of a @fa is the element type of the expected type. The expected type for a @i@fa, or a @fa of a @fa, is the key type of the expected type of the @fa. @s8<@i> The expected type for a @fa shall have an Aggregate aspect that includes a specification for an Add_Unnamed procedure or an Assign_Indexed procedure. The expected type for a @fa that contains one or more @fas with a @i@fa shall have an Aggregate aspect that includes a specification for the Add_Named procedure. The expected type for a @fa that contains one or more @fas shall have an Aggregate aspect that includes a specification for the Add_Named or Assign_Indexed procedure. A @fa can be of any type with an Aggregate aspect. A non-null container aggregate is called an @i if the expected type @i of the aggregate specifies an Assign_Indexed procedure in its Aggregate aspect, and either there is no Add_Unnamed procedure specified for the type, or the aggregate is a @fa with a @fa that contains a @fa or a @fa. The key type of an indexed aggregate is also called the @i of the aggregate. A @fa with a <@> rather than an @fa, or with a @fa that is a @fa, is permitted only in an indexed aggregate. For an @fa without a @i@fa, if the @fa is an indexed aggregate or the expected type of the @fa specifies an Add_Named procedure in its Aggregate aspect, then the type of the loop parameter of the @fa shall be the same as the key type of the @fa. For a @fa that is an indexed aggregate, all @fas shall contain either a @fa, or a @fa without a @i@fa. Furthermore, for such an aggregate, either: @xbullets shall be static expressions or static ranges, and every @fa shall have a @fa that defines a non-null static range, and the set of values of the index type covered by the @fas and the @fas shall form a contiguous range of values with no duplications; or> @xbullet, and if it has a @fa, the list shall have exactly one @fa.> @s8<@i> The evaluation of a @fa starts by creating an anonymous object @i of the expected type @i initialized as follows: @xbullet is an indexed aggregate, from the result of a call on the New_Indexed function; the actual parameters in this call represent the lower and upper bound of the @fa, and are determined as follows:> @xinbull is a @fa, the lower bound is the low bound of the subtype of the key parameter of the Add_Indexed procedure, and the upper bound has a position number that is the sum of the position number of the lower bound and one less than the number of @fas in the @fa;> @xinbull is a @fa, the lower bound is the lowest value covered by a @fa or is the low bound of a range defined by a @fa of a @fa; the upper bound is the highest value covered by a @fa or is the high bound of a range defined by a @fa of a @fa.> @xbullet 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:> @xinbull, the value zero;> @xinbull, the number of @fas;> @xinbull without an @fa, the number of @i@fas;> @xinbull where every @fa contains a @fa, the total number of elements specified by all of the @fas;> @xinbull The evaluation then proceeds as follows: @xbullet, the anonymous object @i is the result;> @xbullet of a type with a specified Add_Unnamed procedure, each @fa is evaluated in an arbitrary order, and the Add_Unnamed procedure is invoked in sequence with the anonymous object @i as the first parameter and the result of evaluating each @fa as the second parameter, in the order of the @fas;> @xbullet that is an indexed aggregate, each @fa is evaluated in an arbitrary order, and the Assign_Indexed procedure is invoked in sequence with the anonymous object @i 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 @fa, and the result of evaluating each @fa as the third parameter;> @xbullet for a type with an Add_Named procedure in its Aggregate aspect, the @fas are evaluated in an arbitrary order:> @xinbull with a @fa, for each @fa of the list in an arbitrary order, the @fa is evaluated as is the @fa of the @fa (in an arbitrary order), and the Add_Named procedure is invoked once for each value covered by the @fa, with the anonymous object @i as the first parameter, the value from the @fa as the second parameter, and the result of evaluating the @fa as the third parameter;> @xinbull with an @fa, the @fa is elaborated, then an iteration is performed as described in 5.5 or 5.5.2, and for each value of the loop parameter of the iteration the Add_Named procedure is invoked with the anonymous object @i as the first parameter, the result of evaluating the @fa as the third parameter, and:> @xI2bull@fa, the result of evaluating the @i@fa as the second parameter;> @XI2Bull @xbullet that is an indexed aggregate, the evaluation proceeds as above for the case of Add_Named, but with the Assign_Indexed procedure being invoked in its stead; in the case of a @fa with a <@> rather than an @fa, the corresponding call on Assign_Indexed is not performed, leaving the component as it was upon return from the New_Indexed function;> @xbullet, the @fas (which are necessarily @fas) are evaluated in the order given; each such evaluation comprises two steps:> @xindent<@xhang<@xterms<1.>the @fa is elaborated;>> @xindent<@xhang<@xterms<2.>an iteration is performed as described in 5.5 or 5.5.2, and for each value of the loop parameter of the iteration, the Add_Unnamed procedure is invoked, with the anonymous object @i as the first parameter and the result of evaluating the @fa as the second parameter.>> @s8<@i> Declarations of Set_Type, Map_Type, and Vector_Type: @xcode< -- @ft<@i> @b Set_Type @b @b Aggregate =@> (Empty =@> Empty_Set, Add_Unnamed =@> Include); @b Empty_Set @b Set_Type;> @xcode< @b Small_Natural @b Natural @b 0..1000;> @xcode< @b Include (S : @b Set_Type; N : @b Small_Natural);> @xcode< -- @ft<@i> @b Map_Type @b @b Aggregate =@> (Empty =@> Empty_Map, Add_Named =@> Add_To_Map);> @xcode< @b Add_To_Map (M : @b Map_Type; Key : @b Integer; Value : @b String);> @xcode< Empty_Map : @b Map_Type;> @xcode< -- @ft<@i> @b Vector_Type @b @b Aggregate =@> (Empty =@> Empty_Vector, Add_Unnamed =@> Append_One, New_Indexed =@> New_Vector, Assign_Indexed =@> Assign_Element);> @xcode< @b Empty_Vector (Capacity : Count_Type := 0) @b Vector_Type;> @xcode< @b Append_One (V : @b Vector_Type; New_Item : @b String);> @xcode< @b Assign_Element (V : @b Vector_Type; Index : @b Positive; Item : @b String);> @xcode< @b New_Vector (First, Last : Positive) @b Vector_Type @b Pre =@> First = Positive'First; -- @ft<@i> -- @ft<@i>> @xcode<@b> @xcode< @b Set_Type @b Bit_Vector (Small_Natural); -- @ft<@i>> @xcode< @b Empty_Set @b Set_Type @b (@b =@> False);> @xcode< @b Int_String_Maps @b @b Ada.Containers.Indefinite_Ordered_Maps -- @ft<@i> (Key_Type =@> Integer, Element_Type =@> String);> @xcode< @b Map_Type @b Int_String_Maps.Map @b;> @xcode< @b Add_To_Map (M : @b Map_Type; Key : @b Integer; Value : @b String) @b Insert;> @xcode< Empty_Map : @b Map_Type := (Int_String_Maps.Empty_Map @b);> @xcode< @b String_Vectors @b @b Ada.Containers.Indefinite_Vectors -- @ft<@i> (Index_Type =@> Positive, Element_Type =@> String);> @xcode< @b Vector_Type @b String_Vectors.Vector;> Examples of container aggregates for Set_Type, Map_Type, and Vector_Type: @xcode<-- @ft<@i> S : Set_Type;> @xcode<-- @ft<@i> S := [];> @xcode<-- @ft<@i> S := Empty_Set;> @xcode<-- @ft<@i> S := [1, 2];> @xcode<-- @ft<@i> S := Empty_Set; Include (S, 1); Include (S, 2);> @xcode<-- @ft<@i>@fa@ft<@i<:>> S := [@b Item @b 1 .. 5 =@> Item * 2];> @xcode<-- @ft<@i> S := Empty_Set; @b Item @b 1 .. 5 @b Include (S, Item * 2); @b;> @xcode<-- @ft<@i>@fa@ft<@i> S := [@b Item @b 1 .. 5 =@> Item, @b Item @b 1 .. 5 =@> -Item];> @xcode<-- @ft<@i> S := Empty_Set; @b Item @b -5 .. 5 @b @b Item /= 0 @b Include (S, Item * 2); @b; @b;> @xcode<-- @ft<@i> M : Map_Type;> @xcode<-- @ft<@i> M := [];> @xcode<-- @ft<@i> M := Empty_Map;> @xcode<-- @ft<@i> M := [12 =@> "house", 14 =@> "beige"];> @xcode<-- @ft<@i> M := Empty_Map; Add_To_Map (M, 12, "house"); Add_To_Map (M, 14, "beige");> @xcode<-- @ft<@i> @b Pair @b Key : Integer; Value : @b String; @b;> @xcode(Positive @b <@>) @b Pair := [(Key =@> 33, Value =@> @b String'("a nice string")), (Key =@> 44, Value =@> @b String'("an even better string"))];> @xcode<-- @ft<@i>@fa -- @ft<@i>@fa@ft<@i<, built from from a table of key/value pairs:>> M := [@b P @b Table @b P.Key =@> P.Value];> @xcode<-- @ft<@i> M := Empty_Map; @b P @b Table @b Add_To_Map (M, P.Key, P.Value); @b;> @xcode<-- @ft<@i> Keys : @b(Positive @b <@>) @b Integer := [2, 3, 5, 7, 11];> @xcode<-- @ft<@i> -- @fa@ft<@i< are of the same type as the key>> -- @ft<@i<(eliminating the need for a separate key_>>@fa@ft<@i<):>> M := [@b Key @b Keys =@> Integer'Image (Key)];> @xcode<-- @ft<@i> M := Empty_Map; @b Key @b Keys @b Add_To_Map (M, Key, Integer'Image (Key)); @b;> @xcode<-- @ft<@i>@fa@ft<@i<:>> M := [@b Key @b Keys @b Key =@> Integer'Image (Key)];> @xcode<-- @ft<@i> V : Vector_Type;> @xcode<-- @ft<@i> V := [];> @xcode<-- @ft<@i> V := Empty_Vector (0);> @xcode<-- @ft<@i> V := ["abc", "def"];> @xcode<-- @ft<@i> V := Empty_Vector (2); Append_One (V, "abc"); Append_One (V, "def");> @xcode<-- @ft<@i> V := [1 =@> "this", 2 =@> "is", 3 =@> "a", 4 =@> "test"];> @xcode<-- @ft<@i> 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");> @xcode<-- @ft<@i> V := [@b I @b 1 .. N =@> Integer'Image (I)];> @xcode<-- @ft<@i> V := New_Vector (1, N); @b I @b 1 .. N @b Assign_Element (V, I, Integer'Image (I)); @b;> @xcode<-- @ft<@i> V := [@b Elem @b M =@> Elem];> @xcode<-- @ft<@i> V := Empty_Vector (); @b Elem @b M @b Add_Positional (V, Elem); @b;> !corrigendum 4.4(3.1/3) @drepl @xindent<@fa@fa<@ ::=@ >@fa@ {|@ @fa}> @dby @xindent<@fa@fa<@ ::=@ >@fa@ {'|'@ @fa}> !corrigendum 13.1.1(5/3) @drepl An @fa identifies an aspect of the entity defined by the associated declaration (the @i); the aspect denotes an object, a value, an expression, a subprogram, or some other kind of entity. If the @fa identifies: @dby An @fa identifies an aspect of the entity defined by the associated declaration (the @i); the aspect denotes an object, a value, an expression, an @fa, a subprogram, or some other kind of entity. If the @fa identifies: !corrigendum 13.1.1(7/3) @dinsa @xbullet shall be an @fa. The expected type for the @fa is the type of the identified aspect of the associated entity;> @dinst @xbullet, the aspect definition shall be an @fa that is an @fa, with the form of the @fa determined by the identified aspect;> !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 ParaSail. [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 s (or unpaired 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 " => value" associations. > > --- > > If we want array and vector aggregates to really be similar, then we > need a named vector aggregate option. This would best be somewhat > separate from the map aggregate, so that the requirements on named > array and vector aggregates are identical. > > Ergo, I'd suggest a third choice (maybe "Add_Indexed") which requires > that the key type is discrete; then choices that are ranges would be > allowed, and non-overlap checking (with the only one dynamic choice > rule) could be enforced. Essentially, the legality rules would match > an array aggregate sans "others" (no others as there is no sane way to > get an applicable index constraint). (The dynamic rules would be essentially > the same as Add_Named). I agree -- Add_Indexed sounds useful, and should be distinct from Add_Named. > --- > > That's all I have at this point. Thanks. I'll try to send out a quick update, with at least some of this. **************************************************************** From: Tucker Taft Sent: Wednesday, February 28, 2018 4:58 PM Steve Baird wrote (privately): > Would any mention of container delta aggregates be premature? Interesting. That sort of makes sense. You'd want that in a post-condition. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 28, 2018 5:44 PM I'd suggest treating that as a separate AI, simply to avoid loading too much stuff into a single AI. (Unless it turns out to be effectively trivial.) But it does seem like it fits into the notion that a Vector aggregate and an array aggregate are as similar in functionality as we can make them. Note that since delta aggregates only work for named notation, such container aggregates would only be meaningful for "Add_Named" and "Add_Indexed" cases. Meaning that Lists and Sets couldn't use them -- annoying, but understandable. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 28, 2018 6:08 PM ... > > You and I have swapped positions on the iterated choices. :-) I now > > think that they ought to be treated like others (which isn't allowed > > here) -- that is, they can appear in positional aggregates, but only > > if they appear last. > > That's the only way to make vector and array aggregates near > > identical, because the iterated choice (except for the "traditional" > > discrete range > > one) doesn't provide the indexes needed for a named array aggregate. > > And all of them (again except for the "traditional" discrete range > > one) can't appear in a named array aggregate without a key > > expression (which is really the index expression for an array aggregate). > > I am not really following this. Could you give an example or two? Array aggregates in Ada have some restrictions on them in order to ease implementation, for instance the only dynamic part of a positional aggregate has to be the last one, and there can only be a single dynamic part in a named aggregate. I've been assuming that we'd want to preserve some version of this going forward (so we aren't allowing arbitrary dynamic iterators in array aggregates) -- if we wanted to relax these sorts of restrictions, we definitely need to do that generally and not just in this one special case. (The effort to implement is about the same for all of these cases.) Anyway, container iterators used in an array aggregate have to be treated as part of a positional aggregate, as they don't provide any index values (so they *can't* be a named aggregate). For example: Some_List : ... -- A list container of Natural. type Some_Array is array (Positive range <>) of Natural; Agg1 : Some_Array := (for E of Some_List => E); -- This has to be treated as a positional array aggregate as the -- choice provide no array indexes. "others" isn't allowed here, -- so we can't even get them from the environment. Agg2 : Some_Array := (for I in Agg1'Range => Agg1(I)); -- This is currently a named aggregate, since it is dynamic it can -- be the only choice here. Agg3 : Some_Array := (for E of Agg1 => E; -- This could go either way, as the indexes could be implied from -- those of Agg1, or ignored (since they're not explicit) Agg4 : Some_Array := (for C in Some_List.Iterator => Some_List.Element(C)); -- Has to be positional, again there is no source for array indexes. Agg5 : Some_Array := (1, 2, 3, for E of Some_List => E); -- A positional aggregate, but no harder to implement than the -- existing cases. Agg6 : Some_Array := (1, 2, for E of Some_List => E, 4, 5); -- A positional aggregate, but with the dynamic part in the middle. -- Ugh. Readability isn't good, either (the trailing choices get -- lost). Ada currently doesn't allow this. Agg7 : Some_Array := (1 => 2, 2 => 4, for E of Some_List => E); -- Hopefully illegal, as the bounds of the iterator aren't defined. -- To make this work, we'd have to "imply" the bounds from the other -- choices. But that would require making the unordered (a named -- array aggregate) ordered in some sense. And then what to do if -- there aren't other choices available? So, we either have to treat these iterators similarly to "others" and allow them (only?) in positional aggregates, or treat them exactly like "others" and allow them only when the bounds come from context. I preferred the first of these when I thought about it, because it would likely to be annoying to have to pre-figure the bounds (and declare an appropriate subtype) when these aggregates are passed as parameters (with unconstrained formal types) or when used in preconditions/postconditions (declaring an bounded subtype might not be possible if other parameters are involved). However this works for arrays, container aggregates ought to be similar, just so we aren't giving unintended incentives to use containers rather than arrays (or vice versa). **************************************************************** From: Tucker Taft Sent: Thursday, March 1, 2018 8:37 AM Here is a new version [this is version /05 of the AI - ED.] that incorporates Randy's idea of "indexed" aggregates, where they require a contiguous no-duplicates range of indices in a named aggregate. This was more work than I originally anticipated, because we need to create the object in advance with bounds determined from the names, and then assign individual values. I introduced the term "indexed aggregates" to avoid having to keep repeating the circumstances under which the new New_Indexed/Assign_Indexed routines would be invoked. The net result of this is that now you can use named aggregates with Vectors, and get the same level of checking as is provided for a named array aggregate (full coverage, etc.). I did *not* try to incorporate Randy's idea of positional aggregate with a single iterated-element-association at the end. It seems easy enough to require the whole aggregate to be named in that case. A mixture of positional and named just seemed too weird, except for the special case of "others => blah" which depends on the "applicable index constraint" notion that doesn't generalize to container aggregates. **************************************************************** From: Randy Brukardt Sent: Thursday, March 1, 2018 10:19 PM ... > I did *not* try to incorporate Randy's idea of positional aggregate > with a single iterated-element-association at the end. It seems easy > enough to require the whole aggregate to be named in that case. A > mixture of positional and named just seemed too weird, except for the > special case of "others => blah" which depends on the "applicable > index constraint" > notion that doesn't generalize to container aggregates. But then you can't use them at all in "Add_Indexed" or array aggregates, because they do not have any way to have bounds. Unless you plan to define a whole new set of bounds rules -- and even that would have to limit them to one iterator per aggregate (I sure hope we're not planning to dump the "one dynamic choice" rule only in this weird case). That doesn't seem to be the way you wanted to go -- but perhaps you've decided to abandon iterators in aggregates except in the map case??? **************************************************************** From: Randy Brukardt Sent: Thursday, March 1, 2018 11:03 PM > Here is a new version ... Now for a few comments on the actual text: >So, to summarise: >* New aspect on types specifying an empty default, plus an > Add_Positional, and either an Add_Named procedure or Assign_Indexed > procedure (with a New_Indexed function to go with it). >* Extend aggregate syntax to trigger these This second bullet seems like 1/2 of a thought. "trigger these" what? There doesn't seem to be any extension of aggregate syntax needed here (that should happen in array aggregates, as previously discussed, and should really happen first so we don't end up with a hodge-podge of rules). >Modify 13.1.1(5/3): > An aspect_mark identifies an aspect of the entity defined by the > associated declaration (the associated entity); the aspect denotes an > object, a value, an expression{, an aggregate}, a subprogram, or some > other kind of entity. If the aspect_mark identifies: Don't we need to add "aggregate" to the syntax? It's rather confusing to have it implicit as part of expression and we already have "name" which also completely overlaps with "expression". Side question: should we go back and change the syntax of Stable_Properties to an aggregate? Or just leave well enough alone?? > key_expression_list ::= /key_/expression {| /key_/expression} No discrete_ranges here for the case of an indexed aggregate (array aggregates definitely allow that). Intentional (and why?) or mistake? There really is a lot of examples; it seems like too many for the RM. (This is inverse of the usual problem.) I have to wonder if we need all of the equivalences in the RM (as opposed to in the !examples section of the AI). But we do need *some* examples in the RM. **************************************************************** From: Tucker Taft Sent: Friday, March 2, 2018 3:00 AM > ... >> I did *not* try to incorporate Randy's idea of positional aggregate >> with a single iterated-element-association at the end. It seems easy >> enough to require the whole aggregate to be named in that case. A >> mixture of positional and named just seemed too weird, except for the >> special case of "others => blah" which depends on the "applicable >> index constraint" >> notion that doesn't generalize to container aggregates. > > But then you can't use them at all in "Add_Indexed" or array > aggregates, because they do not have any way to have bounds. Not sure what you mean by "them" in "can't use them." Did you mean vectors? The proposal does support them, but with a precondition that the low bound matches Index_Type'First. > Unless you plan to define a > whole new set of bounds rules -- and even that would have to limit > them to one iterator per aggregate (I sure hope we're not planning to > dump the "one dynamic choice" rule only in this weird case). That > doesn't seem to be the way you wanted to go -- but perhaps you've > decided to abandon iterators in aggregates except in the map case??? Did you look at the proposal? If so, please clarify your concern. If not, please take a look at the proposal for how Vectors would be supported, and if you have comments on that, let me know. Clearly some array-like containers might allow arbitrary bounds, and others, such as Vector, would require the same low bound for all objects of a given type. I presumed a precondition could be used to enforce the vector-like requirement of a fixed low bound. **************************************************************** From: Randy Brukardt Sent: Friday, March 2, 2018 6:01 PM ... > Not sure what you mean by "them" in "can't use them." Did you mean > vectors? The proposal does support them, but with a precondition that > the low bound matches Index_Type'First. "them" = iterated-element-association. I've mostly been thinking about how they fit into the array aggregate rules. Once that is figured out, then one can use similar rules for container aggregates. Starting with the containers is ass-backwards -- that's a blank slate, whereas with the arrays they have to be fit into existing wording. As I've previously noted, I do not want incentives to use one kind of type over the other (in either direction), so anything available in the containers also has to be available in array aggregates. Aside: The first paragraph of the Legality Rules just provides definitions. Shouldn't that be in Static Semantics? > > Unless you plan to define a > > whole new set of bounds rules -- and even that would have to limit > > them to one iterator per aggregate (I sure hope we're not planning > > to dump the "one dynamic choice" rule only in this weird case). That > > doesn't seem to be the way you wanted to go -- but perhaps you've > > decided to abandon iterators in aggregates except in the map case??? > > Did you look at the proposal? If so, please clarify your concern. If > not, please take a look at the proposal for how Vectors would be > supported, and if you have comments on that, let me know. The idea that a named aggregate uses Add_Positional is bizarre. It works here because of the blank-slate issue, but it will not work with arrays. Well, actually it doesn't work that well here. You now have the possibility of mixed keyed and unkeyed choices (because you are treating an iterator as named even though it doesn't provide any). For instance: A_Map := ("Tuck" => True, "Randy" => False, for E of Some_Bits => E); Assuming that both Add_Named and Add_Positional are supported, this "aggregate" would be implemented with a mix of "named" and "positional" inserts - that seems like madness. (A type supporting both could make sense for a keyed set, for instance.) It's also bizarre (and violates my rule above), that you can write: A_Map := (for E of Some_Bits => E, for E of Some_Other_Bits => E); for a map, but it's illegal for an array or vector. And it's bizarre that you can write: A_List := (for I in 1..1 => True, for I in 1..1 => False, for E of Some_Bits => E); but you're not allowed to write: A_List := (True, False, for E of Some_Bits => E); (since lists are only positional, and the only named choices that a positional type can write are iterators). If we adopted these rules as-is, I predict you'll see the above pretty often. > Clearly some array-like containers might allow arbitrary bounds, and > others, such as Vector, would require the same low bound for all > objects of a given type. I presumed a precondition could be used to > enforce the vector-like requirement of a fixed low bound. I've said repeatedly that I would want to see how these iterators are integrated into the array containers before worrying about anything else. That makes the most sense as positional, at the very least in terms of how the bounds are determined, the legality rules on the indexes are checked, and the like. If you try to add them to named aggregates, you'll need to make a special case for every rule that applies to such an aggregate ("unless it contains an iterator, in which case the bounds are completely different"). If they end up being part of positional aggregates for arrays (definitely the easiest way to deal with them), then they ought to be handled similarly for the container aggregates. End of story. **************************************************************** From: Tucker Taft Sent: Friday, March 2, 2018 7:10 PM >> Not sure what you mean by "them" in "can't use them." Did >> you mean vectors? The proposal does support them, but with a >> precondition that the low bound matches Index_Type'First. > > "them" = iterated-element-association. Oh! I wouldn't have guessed... > I've mostly been thinking about how they fit into the array aggregate rules. > Once that is figured out, then one can use similar rules for container > aggregates. Starting with the containers is ass-backwards -- that's a blank > slate, whereas with the arrays they have to be fit into existing wording. Perhaps, but I don't see anyone screaming for more kinds of array aggregates, whereas aggregates for containers seems like an obvious hole in the current standard. > As I've previously noted, I do not want incentives to use one kind of type > over the other (in either direction), so anything available in the > containers also has to be available in array aggregates. Yeah, but vectors and arrays have one fundamental difference -- vectors are extensible, but of course imply dynamic or bounded allocation. So there is already a pretty strong incentive to use the "right" one, and a few extra syntactic alternatives for the aggregates of one over the other seems OK given that they have this fundamental difference in functionality. > Aside: The first paragraph of the Legality Rules just provides definitions. > Shouldn't that be in Static Semantics? We have allowed definitions to be almost anywhere as far as I know, and certainly we allow them in legality rules. > Unless you plan to define a > whole new set of bounds rules -- and even that would have to limit > them to one iterator per aggregate (I sure hope we're not planning to > dump the "one dynamic choice" rule only in this weird case). That > doesn't seem to be the way you wanted to go -- but perhaps you've > decided to abandon iterators in aggregates except in the map case??? Did you look at the proposal? If so, please clarify your concern. If not, please take a look at the proposal for how Vectors would be supported, and if you have comments on that, let me know. > The idea that a named aggregate uses Add_Positional is bizarre. It works > here because of the blank-slate issue, but it will not work with arrays. I don't see the issue. As pointed out, arrays and vectors are not the same thing, fundamentally because you can add elements to a vector, and you cannot add elements to an array. I think you are over-constraining the solution by requiring them to have identical capabilities. I agree they should be as similar as possible, but we should not have to sacrifice the functionality of one just because non-extensible arrays can't handle them. > Well, actually it doesn't work that well here. You now have the possibility > of mixed keyed and unkeyed choices (because you are treating an iterator as > named even though it doesn't provide any). For instance: > > A_Map := ("Tuck" => True, "Randy" => False, for E of Some_Bits => E); > > Assuming that both Add_Named and Add_Positional are supported, this > "aggregate" would be implemented with a mix of "named" and "positional" > inserts - that seems like madness. (A type supporting both could make sense > for a keyed set, for instance.) That was not my intent. The rules should probably disallow this mixing. In fact, now that we have New_Indexed/Assign_Indexed, we could probably disallow having both Add_Named and Add_Positional for the same type. Set-like types would use Add_Positional, and Map-like types would use Add_Named, and Vector-like types could have both Add_Positional and Assign_Indexed. I can't imagine now a type that would require having both Add_Named and Add_Positional. Even a "keyed set" doesn't gain anything from using named notation, and in fact giving names just creates trouble since you would require that the name matches the key embedded in the value. So I would say we should disallow that combination for now. If someone eventually comes up with an important use for it, we could add it back. > It's also bizarre (and violates my rule above), that you can write: Let's not talk about "rules" yet... > A_Map := (for E of Some_Bits => E, for E of Some_Other_Bits => E); > > for a map, but it's illegal for an array or vector. Not sure why you are saying the above is illegal for a vector. Add_Positional works for a vector. > And it's bizarre that you can write: > > A_List := (for I in 1..1 => True, for I in 1..1 => False, > for E of Some_Bits => E); > > but you're not allowed to write: > > A_List := (True, False, for E of Some_Bits => E); This is mixing positional and named, which is not something we have allowed in the past. I can understand your interest in this, but not supporting it is not "bizarre" in my view. > (since lists are only positional, and the only named choices that a > positional type can write are iterators). If we adopted these rules as-is, I > predict you'll see the above pretty often. Another option is to just rely on concatenation: A_List := (True, False) & (for E of Some_Bits => E); which doesn't seem so bad. Clearly some array-like containers might allow arbitrary bounds, and others, such as Vector, would require the same low bound for all objects of a given type. I presumed a precondition could be used to enforce the vector-like requirement of a fixed low bound. > I've said repeatedly that I would want to see how these iterators are > integrated into the array containers before worrying about anything else. I think you are beginning to get a bit autocratic here. I understand your desire to keep nearly equivalent functionality, but you are starting to sounds like these are uncompromisable demands. Let's keep this to "in my view..." or something. The veto-like terminology gets old... > That makes the most sense as positional, at the very least in terms of how > the bounds are determined, the legality rules on the indexes are checked, > and the like. If you try to add them to named aggregates, you'll need to > make a special case for every rule that applies to such an aggregate > ("unless it contains an iterator, in which case the bounds are completely > different"). > > If they end up being part of positional aggregates for arrays (definitely > the easiest way to deal with them), then they ought to be handled similarly > for the container aggregates. End of story. Again, there are multiple views here. I am happy to be part of a search for solutions that make us all happy, but lets not start making threats. It really doesn't help. **************************************************************** From: Randy Brukardt Sent: Saturday, March 3, 2018 11:06 PM >> I've mostly been thinking about how they fit into the array aggregate rules. >> Once that is figured out, then one can use similar rules for >> container aggregates. Starting with the containers is ass-backwards >> -- that's a blank >> slate, whereas with the arrays they have to be fit into existing wording. > Perhaps, but I don't see anyone screaming for more kinds of array aggregates, > whereas aggregates for containers seems like an obvious hole in the current > standard. Doesn't seem that "obvious" to me; the best feature of aggregates is the completeness checks but those don't make any sense for containers. Thus, container aggregates seem logical but hardly critical. >> As I've previously noted, I do not want incentives to use one kind of type >> over the other (in either direction), so anything available in the >> containers also has to be available in array aggregates. > Yeah, but vectors and arrays have one fundamental difference -- vectors are > extensible, but of course imply dynamic or bounded allocation. So there is > already a pretty strong incentive to use the "right" one, and a few > extra syntactic alternatives for the aggregates of one over the other > seems OK given that they have this fundamental difference in functionality. ??? Extensibility doesn't have much to do with aggregates, because they are an all-at-once construction of an object. And arrays can be unconstrained, so aggregates with variable lengths make sense for both. Thus, they seem like two views of the same thing to me. >> Aside: The first paragraph of the Legality Rules just provides definitions. >> Shouldn't that be in Static Semantics? > We have allowed definitions to be almost anywhere as far as I know, > and certainly we allow them in legality rules. That paragraph seems out of place to me, as it has nothing to do with legality. (And not much to do with the actual legality rules, either.) When I wrote that, I thought there was a Static Semantics section, and I thought the definition fit better there. But I can't find that section now, so I apparently imagined that. ... >> The idea that a named aggregate uses Add_Positional is bizarre. It >> works here because of the blank-slate issue, but it will not work with arrays. > I don't see the issue. Obviously. :-) > As pointed out, arrays and vectors are not the same thing, > fundamentally because you can add elements to a vector, and you cannot > add elements to an array. Which has precisely nothing to do with a construct for constructing an object from scratch. > I think you are over-constraining the solution by requiring them to > have identical capabilities. I agree they should be as similar as > possible, but we should not have to sacrifice the functionality of one > just because non-extensible arrays can't handle them. Who said anything about "sacrificing" anything? All I've said is that I want the rules to be essentially the same for both. That doesn't require any functionality to be dropped. ... >> It's also bizarre (and violates my rule above), that you can write: > > Let's not talk about "rules" yet... Substitute "principle" then. Sheesh. >> >> A_Map := (for E of Some_Bits => E, for E of Some_Other_Bits => E); >> >> for a map, but it's illegal for an array or vector. > > Not sure why you are saying the above is illegal for a vector. > Add_Positional works for a vector. You have a Legality Rule that says that an iterator can only occur once for a indexed aggregate type. That seems to prevent the above. (If it doesn't, I'd object even louder, because the form of an iterator certainly shouldn't change the legal uses.) >>And it's bizarre that you can write: >> >> A_List := (for I in 1..1 => True, for I in 1..1 => False, >> for E of Some_Bits => E); >> >> but you're not allowed to write: >> >> A_List := (True, False, for E of Some_Bits => E); > >This is mixing positional and named, which is not something we have >allowed in the past. I can understand your interest in this, but not >supporting it is not "bizarre" in my view. You've missed my point: people WILL write the former because they aren't allowed to write the latter. That's what's "bizarre". I'd be happy to make both of them illegal -- it just doesn't make sense to allow a really complex way to write something and not allow a much simpler way to write the same thing. >>(since lists are only positional, and the only named choices that a >>positional type can write are iterators). If we adopted these rules >>as-is, I >>predict you'll see the above pretty often. >Another option is to just rely on concatenation: > > A_List := (True, False) & (for E of Some_Bits => E); > > which doesn't seem so bad. ...except the lists don't have "&". And even if they did have "&", the above requires creating three objects and copying all of the contents of two of them. That's a lot of unnecessary overhead. ... >> I've said repeatedly that I would want to see how these iterators are >> integrated into the array containers before worrying about anything else. > > I think you are beginning to get a bit autocratic here. I understand your > desire to keep nearly equivalent functionality, but you are starting to sounds > like these are uncompromisable demands. Let's keep this to "in my view..." or > something. The veto-like terminology gets old... Huh? I said "I would want", which is a fact. You can ignore that "want" if you like, but I don't view this as something that I am likely to compromise much on. Moreover, it's possible that having worked out the array rules, we'll find that it makes more sense to structure the aggregates as you have proposed. I I just want to have that (array rules) done first, before we finalize too much of these container rules. They make the organization of the container rules be more sensible. >>That makes the most sense as positional, at the very least in terms of >>how the bounds are determined, the legality rules on the indexes are >>checked, and the like. If you try to add them to named aggregates, >>you'll need to make a special case for every rule that applies to such >>an aggregate ("unless it contains an iterator, in which case the >>bounds are completely different"). >> >>If they end up being part of positional aggregates for arrays >>(definitely the easiest way to deal with them), then they ought to be >>handled similarly >>for the container aggregates. End of story. > >Again, there are multiple views here. I am happy to be part of a search for >solutions that make us all happy, but lets not start making threats. >It really doesn't help. I don't see any threat here, certainly wasn't making one. I have just said repeatedly that I want to see how the new iterators fit into array aggregates (which already have a pile of rules) before finalizing the container rules (which are more of a blank page). Otherwise, we're likely to end up with something that is more different than necessary. I agree that having the iterators be "positional" is somewhat weird -- it was my complaint with your first draft of this AI. But more thought on the subject shows that that is the only realistic possibility (which you implicitly agree with by having them use the Add_Positional routine). I also realized that we already have "others => expr" in the array positional cases, so it hardly would be unusual to have more such cases. Array aggregates are syntactically divided into positional and named (indexed for the containers) cases, and the rules are almost all different for the two cases. I find the mixing in your current container proposal (where the syntax is divided, but then the rules end up being blended together in an almost arbitrary manner) uncomfortable. On top of which, it prevents reasonable container aggregates unless someone writes them with phony iterators. How does that help? **************************************************************** From: Randy Brukardt Sent: Friday, March 9, 2018 11:31 PM A couple of extra thoughts on this, noted when filing these messages tonight: > Tucker writes: > > >> I've mostly been thinking about how they fit into the array > >> aggregate rules. Once that is figured out, then one can use similar > >> rules for container aggregates. Starting with the containers is > >> ass-backwards > >> -- that's a blank slate, whereas with the arrays they have to be > >> fit into existing wording. > > > Perhaps, but I don't see anyone screaming for more kinds of array > > aggregates, whereas aggregates for containers seems like an obvious > > hole in the current standard. I responded: > Doesn't seem that "obvious" to me; the best feature of aggregates is > the completeness checks but those don't make any sense for containers. > Thus, container aggregates seem logical but hardly critical. I should point out that we're really discussing the iterator cases here. Most of the "problem" to be solved by container aggregates are solved by simple positional or mapped aggregates: A_List := (A, B, C); A_Map := ("Randy" => A, "Tucker" => B); I have no problem with the portion of the solution involving them (it looks fine to me). OTOH, adding container iterators into this mix is not so obvious, neither in terms of the need or in terms of how they fit into the existing kinds of aggregates or even how they fit into the proposal itself. Tucker has been ascribing nearly magical properties to various uses of these iterators, in ways that tend to trigger my BS detector. That's tempered by the fact that Tucker usually isn't the sort to rave on crazily. Still, anyone can get over-enthusiastic about something (that's probably how I got here in the first place ;-). *IF* it was up to me solely, I would treat the solution to the obvious container aggregate problem separate from the question of adding containers iterators to *any* aggregates. And, the AI for that latter case, I would add the iterators to both array and container aggregates at the same time. (If it's good for the one, it has to be good for the other -- they're different views of the same thing, the only difference being that arrays are built in.) I think any other path for additional iterators to be folly for many reasons, the most obvious being that creating an array from a container should be no harder than creating a container from an array. This shouldn't be considered a demand - if I'm the only one that feels this way, I can take a hint... Aside: I like how Tucker snuck in a notation for an empty container aggregate into his container AI, despite already having another AI open for that, and despite that other AI not getting much traction. I'm going to resist saying more... ... > > Yeah, but vectors and arrays have one fundamental difference -- > > vectors are extensible, but of course imply dynamic or bounded > > allocation. So there is already a pretty strong incentive to use > > the "right" one, and a few extra syntactic alternatives for the > > aggregates of one over the other seems OK given that they have this > > fundamental difference in functionality. I said: > ??? Extensibility doesn't have much to do with aggregates, because > they are an all-at-once construction of an object. > And arrays can be unconstrained, so aggregates with variable lengths > make sense for both. Thus, they seem like two views of the same thing > to me. To expand a bit, it's clear that the underlying implementation of containers aggregates use operations to add the elements to the container one at a time. That's where the extensibility comes into play. However, the programmer model for an aggregate is that it is built all at once -- one hopefully is not looking under the hood here to see how it *really* is implemented. Since the "thought" model for an aggregate is something built as a single object all at once, extensibility doesn't come into play. It would come into play if you wrote a normal for loop statement to fill the container: for E of Some_Array loop Append (New_Container, E); end loop; but an aggregate iterator is logically done all at once in an arbitrary order; there's no implied order involved. (Indeed, it would be a candidate for as-if parallel operation if such a thing actually made sense on typical hardware.) **************************************************************** From: Tucker Taft Sent: Saturday, March 10, 2018 11:31 PM > ... > Aside: I like how Tucker snuck in a notation for an empty container > aggregate into his container AI, despite already having another AI > open for that, and despite that other AI not getting much traction. > I'm going to resist saying more... The more I worked on the "(null T)" idea for an empty aggregate of type T, the less I liked it. Since the container aggregate required the specification of an "Empty" operation, it made sense to me to include a notation for an empty aggregate as part of the container aggregate AI, and make it something that didn't require the full name of the type to be buried in the aggregate. We generally don't allow you to look inside an aggregate until you know exactly what type it is, so it seemed particularly silly to repeat the full name of the type inside an empty aggregate. Once we leave out the full name of the type from the "empty" aggregate, the notation that kept seeming the most obvious to me for things like "empty set" and "empty map" was "(<>)". **************************************************************** From: Tucker Taft Sent: Saturday, March 10, 2018 9:58 AM > ... > I should point out that we're really discussing the iterator cases here. > Most of the "problem" to be solved by container aggregates are solved > by simple positional or mapped aggregates: > A_List := (A, B, C); > A_Map := ("Randy" => A, "Tucker" => B); > > I have no problem with the portion of the solution involving them (it > looks fine to me). > > OTOH, adding container iterators into this mix is not so obvious, > neither in terms of the need or in terms of how they fit into the > existing kinds of aggregates or even how they fit into the proposal itself. ... We added iterators to array aggregates after 20 years, and they were sorely missed. The point is that you often want to create an aggregate where the components are systematically a function of the position, or something else that is varying. Turning your logic around, it would be weird to allow the following for an array aggregate but not for a vector aggregate: (for I in 1..N => I**2) The *only* controversy seems to be whether we should require supporting container iterators in array aggregates, and that seems to be only a controversy for you. Everyone else seems to see it as clear that we should focus on allowing container iterators in container aggregates, since a frequent use (in my experience, as well as the experience in using similar constructs in Python and other languages), is that you want to create a new container that is an element-wise function of some other container. E.g., given a set, you want to create a new set that contains, say, the square of each element, or the mapping of each element to some other type, ... There is less need to construct an array as an element-wise function of a container in my experience, and the added expense of having to pre-allocate the array makes this less attractive from an implementation point of view. I am happy to add this feature, but it feels like a separate issue, with more complex implementation requirements, and I wouldn't want to "sink" the container aggregate idea overall just because supporting an array as an element-wise function of a container requires a more complex implementation. **************************************************************** From: Randy Brukardt Sent: Saturday, March 10, 2018 7:37 PM ... > > OTOH, adding container iterators into this mix is not so obvious, > > neither in terms of the need or in terms of how they fit into the > > existing kinds of aggregates or even how they fit into the > proposal itself. ... > > We added iterators to array aggregates after 20 years, and they were > sorely missed. Not by me. I think there were a handful of cases in my 40 years of programming where they would have helped. Definitely a non-zero need, but hardly something critical. ... The point is that you often want to > create an aggregate where the components are systematically a function > of the position, or something else that is varying. > Turning your logic around, it would be weird to allow the following > for an array aggregate but not for a vector aggregate: > > (for I in 1..N => I**2) Wouldn't mind this at all, so long as it follows array iterator rules -- that is, it calls Add_Indexed in your current draft. ... > The *only* controversy seems to be whether we should require > supporting container iterators in array aggregates, and that seems to > be only a controversy for you. Everyone else seems to see it as clear > ... That is clearly your imagination; we've never discussed this topic at a meeting so there is no possible way to know what "everybody else" is thinking. My recollection was that this was introduced at a meeting as "what we should do" without any real opportunity to digest it ahead of time. ... > There is less need to construct an array as an element-wise function > of a container in my experience, How do you know this? We've never had such iterators in Ada, and other languages that have such iterators treat arrays similarly to all of the other containers -- so how could you tell the difference? An array is just a container that is built-in for historical reasons, treating it differently is madness. --- In any case, if "everybody else" agrees with you, then you should clearly ignore me. Which is essentially what you've been doing on this topic, so it isn't worth any more of my time in this venue. (We can fight it out at whatever meeting this AI gets discussed.) To summarize, I can't support the current proposal for a myriad of reasons: (1) The proposal is unnecessarily different than array aggregates: The iterators allowed and the interpretation is different for the arrays and the containers, ranges aren't allowed as indexed choices for no known reason, nor is <> allowed for the named forms (why a default initialized component is valuable for an array and not for a container escapes me). (2) <> in an aggregate means "default initialize", and to use it to represent an empty container is a nasty collision of meanings in essentially the same construct. (Ideally, "<>" would mean default-initialization everywhere, but that was a bridge-too-far in the past...I don't want to prevent such a usage.) (3) There's a separate AI for empty aggregates, and there is no good reason to put that into this one -- Empty_List is a fine way to get an empty list container. (Not to mention that writing such containers explicitly isn't going to happen very often in any case.) Besides, there is no sane way (not even a function) to write a 1-element list (as a positional-only aggregate, the only choice in the current AI is a phony iterator: My_List := (1); -- Not an aggregate. My_List := (for I in 1..1 => 1); -- OK, but insane.) So there is no real "consistency" argument for the need for empty aggregates. (4) The AI implements "named" aggregates with the "positional" implementation (the Add_Positional subprogram). This weird mixing of concepts is very uncomfortable. (5) The AI as written allows elements implemented by positional and named routines in the same aggregate. (You did promise to fix this.) I'm not against this concept per-se, but I'm definitely against this implementation of the concept. **************************************************************** From: Tucker Taft Sent: Sunday, March 11, 2018 10:47 AM > ... > I'm not against this concept per-se, but I'm definitely against this > implementation of the concept. I'll take another shot at this AI, and hopefully can reach some kind of middle ground here. I would point out that in many languages, (non-extensible) arrays and (extensible) vectors are two different things, and treated quite differently. But I'll try to make the aggregates more consistent between them in the next version of the AI. **************************************************************** From: Jeff Cousins Sent: Monday, March 12, 2018 10:23 AM >> We added iterators to array aggregates after 20 years, and >> they were sorely missed. >Not by me. I think there were a handful of cases in my 40 years of > programming where they would have helped. At least I agree with Tucker on this bit, I would have used almost daily. >> The *only* controversy seems to be whether we should require >> supporting container iterators in array aggregates, and that >> seems to be only a controversy for you. Everyone else seems >> to see it as clear ... >That is clearly your imagination; we've never discussed this topic at a >meeting so there is no possible way to know what "everybody else" is >thinking. The only recent-ish messages seem to have been from Randy and Tucker. Personally I’m confused as to what exactly is being proposed here – and I suspect that it’s the same for some pf the other members, hence why they’re not commenting – and would have to abstain if it came to a vote. **************************************************************** From: Edmond Sconberg Sent: Monday, March 12, 2018 11:15 AM I share Jeff's sense of confusion, I am all in favor of the proposed container aggregates and the new attributes for indexed, named, and keyed insertions. I don’t know what the exact proposal is for new array aggregates. I agree that it is worthwhile generalizing the use of container iterators wherever a loop is implied. (the issue of the size of an array aggregate built with an iteration over a container is an implementation question, the point is that the notation is useful). If the context is an array type, and S is a hashed set, I can make sense of (for X of S => F (X)), but given that the iteration over S produces values in some unspecified order I’ve created an array with elements in unpredictable order. Not sure that this is particularly useful. If we really need to produce indexable sequences from other containers, vectors may be all we need. Finally, I find (<>) perfectly acceptable as a notation for an empty container! **************************************************************** From: Randy Brukardt Sent: Monday, March 12, 2018 2:48 PM > I share Jeff's sense of confusion, I am all in favor of the proposed > container aggregates and the new attributes for indexed, named, and keyed > insertions. I don’t know what the exact proposal is for new array > aggregates. I don't know what the exact proposal is either, because there hasn't been one. Which is the core of my objection - the array case is harder to define since it has to fit into the existing rules. So I'd like to see that before getting too far into the containers aggregate proposal - it might make sense to make the rules similar for the two cases. **************************************************************** From: Tucker Taft Sent: Monday, March 12, 2018 5:13 PM A new version is forthcoming... **************************************************************** From: Tucker Taft Sent: Tuesday, March 13, 2018 5:23 PM OK, here is another shot at container aggregates, including a section on generalizing array aggregates. [This is version /06 of the AI - ED.] I still didn't allow having a positional container aggregate with an iterator part at the end. Still feels weird to me, but could be added later if the ARG as a whole really wants it. Other major changes -- now call the operation "Add_Unnamed" rather than "Add_Positional," since it is used in both positional and named aggregates. Don't allow both Add_Unnamed and Add_Named for the same type. Allow "<>" in "indexed" aggregates. Allow discrete ranges in indexed aggregates. Removed a few of the overly complex examples. **************************************************************** From: Randy Brukardt Sent: Wednesday, March 28, 2018 8:27 PM > OK, here is another shot at container aggregates, including a section > on generalizing array aggregates. Thanks. A few thoughts. (1) I find this better, still not perfect (but when does that happen? :-) (2) I still dislike using (<>) for empty aggregates, especially when <> means something else. I won't belabor this, I've covered it before. [(7) covers one possible way to eliminate this objection.] (3) <> in array and record aggregates means a default-initialized component. Here, you have defined it to mean an empty (uninitialized) slot instead. These aren't the same thing! (It's a Bounded Error to read an uninitialized slot; a default-initialized element is usable in the normal way.) I use default-initialized aggregate items all the time with the explicit intent of getting default initialization (typically, so counters/lengths and the like are initialized to empty). Note that most of the (definite) containers have Insert operations that don't take an element and insert a default initialized object. (See, for instance, A.18.2(163-165/2)). I don't know that it is worth explicitly using these operations, but that fact that they were considered valuable enough to define suggests to me at least that <> meaning default-initialization is also valuable. It is clear that <> is only meaningful for definite types (that is a requirement of record and array types, not for containers). So, as an alternative proposal, consider the following outline: * <> is only allowed when the element type is definite; * <> is allowed for any construct in a named aggregate; * For Add_Named, <> is equivalent to the block: declare Elem : Element_Type; -- Default-initialized; begin Add_Named (Container, Key, Elem); end; * For Add_Indexed, <> is equivalent to the block: declare Elem : Element_Type; -- Default-initialized; begin Add_Named (Container, Index, Elem); end; * If Add_Unnamed is needed, it would follow as the above. We can discuss this further later. (4) There's some typos in the array iterator proposal: the clause number is incorrectly 4.2.3 in a couple of places, and I presume that "Add 4.2.3(20) as follows:" should really be "Add after 4.3.3(20):" since I presume you are not suggesting to replace 4.3.3(20) [and there is no 4.2.3 anyway]. (5) The wording change (presumably) added after 4.3.3(20) seems a bit more explicit than necessary. Do we have to *require* that the iteration is performed twice? We surely should allow it, but if the compiler can figure the length some other way (or is willing/able to use an expandable temporary space), shouldn't we allow only a single execution of the iterator? (An as-if optimization isn't enough, as one can detect if a container iterator is executed.) In particular, if one uses the array element iterator in this way (surely legal), the length can be figured from the array without executing any iteration. That is: for E of Some_Array => the compiler can surely figure out how many elements there are without executing anything. Similarly, for the language-defined containers, the length of the container element iterator can be figured by asking the container length without iterating anything: for E of Some_Container => The number of elements in this case is always Some_Container.Length for the language-defined containers. This latter optimization would require some magic as it stands (although we could define a language-defined enhancement if we wanted to make this a standard thing). As an implementor, I'd probably define an implementation-defined aspect and specify that with Length. Since iteration can be expensive, that would be a better option for these common iterators. Of course, arbitrary cursor-based iterators would probably require a count. For something like: for C in Some_Container.Iterator (From => First, To => Last) loop the number of elements is unknown. (6) I didn't see anything to deal with the 1-element aggregate for containers that only define Add_Unnamed. As we all know, there is no single element positional aggregate. Containers that only define Add_Unnamed (like lists and sets) can't fall back on named notation, as there are no names to use. The only solution in the current AI is to use some sort of iterator: (for I in 1 .. 1 => The_Element) If users think that saying "(1 .. 0 => The_Component)" is annoying for the rarely-used null array aggregate, they ought to scream bloody murder for being forced to write the above. I'm definitely against defining any empty aggregate if we don't fix this problem, as single element lists seem a lot more likely to me than empty lists. If you have to use a function to sensibly create the single element lists, then there cannot be any worse hardship to do the same for the less likely empty lists. (That is, there isn't much point in plugging one hole if we leave a worse one.) I would personally prefer to add the missing functions to List and Set to create one-element lists and forget anything about empty aggregates (here and for null arrays), but I'd be happy to figure better solutions. (7) One wild thought I just had was that we don't really need an Empty parameter to the Aggregate aspect -- all of the language-defined containers require the default-initialized container to be "empty". If we simply said that the container object is default-initialized, this would mean "empty" for the language-defined containers, then (<>) would actually mean a default initialized container -- which would eliminate my primary objection to using the notation for an empty container aggregate. (Sorry, this doesn't work for other aggregates -- <> only is meaningful for definite types.) It also would simplify the semantics description slightly - one less subprogram to check and describe rules for. A user-defined container that wanted to use the aggregate notation would have to define the default-initialized state to be something useful as the start of a container. Perhaps I'm lacking imagination here, but I can't see a sensible way for a container to start with elements in it (where would they come from?) and still have adding elements be particularly meaningful. So this suggestion would reduce the flexibility a bit, but probably wouldn't make much difference in practice. **************************************************************** From: Tucker Taft Sent: Wednesday, March 28, 2018 8:57 PM >> OK, here is another shot at container aggregates, including a section >> on generalizing array aggregates. > > Thanks. A few thoughts. ... Thanks, all good comments, and no particular responses at this point. It feels like we are on the same wavelength, even if some of the details might be up for debate. **************************************************************** From: Randy Brukardt Sent: Wednesday, March 28, 2018 9:14 PM Comments are welcome; I can justify debating this proposal's details much more easily than anything involving rarely-used contracts. ;-) **************************************************************** From: Jeff Cousins Sent: Saturday, July 7, 2018 3:52 PM a few minor comments that I didn’t raise at the meeting as the AI was going back for a re-write anyway... 4.3.5 Dynamic Semantics 3rd bullet – “as the low bound of a range” -> “is the low bound of a range”? 8th bullet (for a named_container_aggregate) – “is of the form with” – I don’t find this terribly clear, though maybe just changing “with” to “containing” would suffice. 15th (for a container_element_association) and final (the iterated_element_association) bullets – both of these have two “and”s – maybe the first “and” should go, and/or possibly the second “and” should be a “then”?? Examples Various places seem to be missing a new-line and bolding, e.g. “type Set_Type is private”, the various “S := ...”s. **************************************************************** From: Tucker Taft Sent: Wednesday, October 17, 2018 8:32 PM Here is an update to the container aggregate AI, switching over to using square brackets "[...]" for such aggregates. [This is version /07 of the AI - Editor.] This AI also permits the use of "[...]" for array aggregates and array delta aggregates. It also permits the use of container iterators within array aggregates. Finally, we switch from using "," to separate off an explicitly specified key to using the reserved word "use" (after much internal debate). The "for ... use ..." combination appears elsewhere jn the language, so it seemed like a reasonable choice here. It also seemed to work in the presence of iterator filters, if we ever incorporate them. E.g.: [for P of C when Is_Good(P) use P.Key => P.Value] **************************************************************** From: Brad Moore Sent: Thursday, October 18, 2018 12:24 AM Thanks for the update Tuck, Just a note; It looks to me like iterated_element_association should be added to the list in 3.1(6/5), just as iterated_component_association is already mentioned there. **************************************************************** From: Randy Brukardt Sent: Thursday, October 18, 2018 8:09 PM One thing I immediately noticed is missing here is an update to 2.2 to add square brackets as an acceptable delimiter. That is, we need to add square brackets to the lists in 2.2(9/5) and 2.1(15/5). Else they're not acceptable syntax. There's a second problem, and that is that our syntax form uses brackets to denote optional items. We need to think of some way to differentiate literal brackets from the use as a syntax form. (As it stands, your syntax changes make aggregates optional. :-) 1.1.4 has a hack for the vertical bar, that it stands for itself when it appears immediately after a curly bracket. That sort of hack won't work here, but we need *something*. Didn't notice anything else. **************************************************************** From: Randy Brukardt Sent: Thursday, October 18, 2018 8:36 PM Reading some of Brad's text, and started wondered about how qualified expressions work with container aggregates. It looks like one can write: Attendance : constant My_Vector := My_Vector'["Tuck" => True, "Randy" => True, "Jeff" => False, ... ]; to qualify a container aggregate. That's because the syntax of a qualified expression is: qualified_expression ::= subtype_mark'(expression) | subtype_mark'aggregate Certainly, that's better than: Attendance : constant My_Vector := My_Vector'(["Tuck" => True, "Randy" => True, "Jeff" => False, ... ]); but it's likely to be surprise to some (it was to me, until I looked up the exact syntax of qualified expression). Maybe we ought to have some examples like this. **************************************************************** From: Tucker Taft Sent: Thursday, October 18, 2018 8:49 PM >> Here is an update to the container aggregate AI, switching over to >> using square brackets "[...]" for such aggregates. > > One thing I immediately noticed is missing here is an update to 2.2 to > add square brackets as an acceptable delimiter. That is, we need to > add square brackets to the lists in 2.2(9/5) and 2.1(15/5). Else > they're not acceptable syntax. Good point. > There's a second problem, and that is that our syntax form uses > brackets to denote optional items. We need to think of some way to > differentiate literal brackets from the use as a syntax form. (As it > stands, your syntax changes make aggregates optional. :-) I was presuming we could use a distinct font for the html/pdf versions (probably make the []'s used for optionality taller and thinner), and in text, something like '[' and ']' for uses of square bracket characters. > 1.1.4 has a hack for the vertical bar, that it stands for itself when > it appears immediately after a curly bracket. That sort of hack won't > work here, but we need *something*. See above for a suggestion. > Didn't notice anything else. OK, good to hear that. **************************************************************** From: Tucker Taft Sent: Thursday, October 18, 2018 9:25 PM > I was presuming we could use a distinct font for the html/pdf versions > (probably make the []'s used for optionality taller and thinner), and > in text, something like '[' and ']' for uses of square bracket > characters. I'm pretty sure ISO will not be happy if we start using even more fonts. (Time for Comic Sans! :-) And doing it that way does mean a significant programming session for me, since some new capabilities would have to be added to the RM generator/'corrigendum processor/AI display generator to use a different font (or even bold vs. regular), in particular new markup. This isn't exactly the best time to engage in a new project of that sort. (I figure I have about 540 hours of possible work time between the end of this meeting and the time we're supposed to deliver the RM -- and I don't know yet how much of that time will be funded. I probably already have work to use much of it in any case.) **************************************************************** From: Randy Brukardt Sent: Tuesday, November 20, 2018 1:05 AM Jeff had sent some comments: >!problem >“post condition” -> postcondition. Yup. >!proposal >Normally (but not always) we capitalise the first letter of a comment in >examples. Yup. I got rid of the TBD parts of the comments, as we're not currently thinking about any of these other ideas. I changed those to "Not proposed now (not in this AI)". Also, there is a filter example. I changed that to refer to AI12-0250-1 and to make it clear that it too is not in this AI. >!wording >Presumably “Add 4.3.3(20)” should be “Add after 4.3.3(20)” as there already >is a 4.3.3(20). Well, it really is "Add before 4.3.3(21)" because it needs to be under the Dynamic Semantics heading. >And as sent previously: > >4.3.5 > >Dynamic Semantics >3rd bullet – “as the low bound of a range” -> “is the low bound of a range”? The upper bound uses similar wording using "is", so I think "as" is just a typo. >8th bullet (for a named_container_aggregate) – “is of the form with” – >I don’t find this terribly clear, though maybe just changing “with” to >“containing” would suffice. I just got rid of the whole mess replacing it with "containing". (Why the "is of the form"? It either contains a loop_parameter_specification or it doesn't, and we're already talking syntactally.) This leaves us with: * for a named_container_aggregate where every iterated_element_association contains a loop_parameter_specification, ... >15th (for a container_element_association) and final (the >iterated_element_association) bullets – both of these have two “and”s – >maybe the first “and” should go, and/or possibly the second “and” >should be a “then”?? I thought it was the first "and" that should be replaced by a "then". The individual loop parameter values are those generated by the iteration (thus the "and"). But really the phrase "then an iteration is performed" (originally "and an iteration is performed") is very vague. I think we need to say something about how an iteration is performed. Obviously we don't want to say any more than absolutely required. Perhaps something like "then an iteration is performed as described in 5.5 or 5.5.2"? That's what I used, at least (and similarly in the 4.3.3(23.2/3) wording). ---- I was directed to add a rule that the meta-symbols in the grammar notation stood for themselves if surrounded in single quotes, and delete the old hack. I also added the missing definition of the square bracket delimiters. This resulted in the following additional wording: >Modify 1.1.4(12): > >* A vertical line separates alternative items[ unless it occurs >immediately > after an opening curly bracket, in which case it stands for itself]: > >Delete the second example (discrete_choice_list) in 1.1.4(13). > >Add after 1.1.4(13): > >* For symbols used in this notation (square brackets, curly brackets, and > the vertical line), the symbols when surrounded by ' represent themselves: > > discrete_choice_list ::= discrete_choice {'|' discrete_choice} > named_container_aggregate ::= '[' container_element_association_list ']' > >Add [ ] to the list of delimiters in 2.2(9/5). > >Replace "{|" with "{'|'" in 3.7.1(3), 3.8.1(4), 4.3.1(5), 4.4(3.1/3), >and 11.2(3). **************************************************************** From: Tucker Taft Sent: Tuesday, November 21, 2018 9:06 AM All makes sense to me... **************************************************************** From: Randy Brukardt Sent: Wednesday, January 23, 2019 6:17 PM The wording of AI12-0212-1 has: Modify 4.3.3(30): For a multidimensional array_aggregate, a check is made that all subaggregates that correspond to the same index have the same bounds. {For a null aggregate used to define the value of a multidimensional array type, bounds for each dimension are determined as for a positional_array_aggregate with zero expressions.} But this placement doesn't make much sense: paragraphs 28-31 are about checks, not determining bounds. Paragraphs 24-27 are about determining bounds. Since a null aggregate is a special case of a positional_array_aggregate, it makes more sense to add this new sentence after 4.3.3(26) (the paragraph about positional_array_aggregates). I note that the wording is a bit screwy, in that a null aggregate *is* a positional_array_aggregate. So I added "for each dimension" to the end to make it a bit clearer. It also seems that we need to exclude the null aggregates from the preceding paragraph, and handle all kinds of null aggregates separately. That gives: For a positional_array_aggregate Redundant[(or equivalent string_literal)] without an others choice{ that is not a null aggregate}, the lower bound is that of the corresponding index range in the applicable index constraint, if defined, or that of the corresponding index subtype, if not; in either case, the upper bound is determined from the lower bound and the number of expressions Redundant[(or the length of the string_literal)]; {For a null aggregate, bounds for each dimension are determined as for a positional_array_aggregate with zero expressions for each dimension; AARM Reason: We need a separate rule to describe what happens for a multidimensional null aggregate; the single dimension case directly follows from the positional_array_aggregate definition.} I made these changes, since they don't make any semantic difference. (Consider it my Editorial Review comments.) Aside: 4.3.3(27) seems to cover the cases already covered in the other new bullet in this text. So I changed it slightly: For {any other}[a] named_array_aggregate without an others choice, the bounds are determined by the smallest and largest index values covered by any discrete_choice_list. P.S. If we had made null aggregate a separate syntactic entity, we could avoid some of the mess. That is, if we defined null_array_aggregate := '[' ']' then we wouldn't need the textual definition of "null aggregate", we wouldn't need the exception in 4.3.3(26), the 4.3.3(26.1) bullet would look more similar to the others (and we could drop the AARM note), and so far as I can tell, the only other paragraph that could need a bit of change would be 4.3.3(20): A subaggregate that is a string_literal is equivalent to one that is a positional_array_aggregate of the same length, with each expression being the character_literal for the corresponding character of the string_literal. And here we could just use a TBH note, since it has been this way since time-immemorial even though there never were any positional_array_aggregates of length 0 or 1 until Ada 2020. Or we could clean up this old oddity by mentioning a null_array_aggregate: A subaggregate that is a string_literal is equivalent to one that is a positional_array_aggregate of the same length (or a null_array_aggregate if the length is zero), with each expression being the character_literal for the corresponding character of the string_literal. I didn't do this because changing the syntax seemed like a bit too much change to do as "editorial". (Note this is how null container aggregates are handled.) **************************************************************** From: Randy Brukardt Sent: Thursday, January 24, 2019 7:39 PM Toward the end of the Dynamic Semantics, we have: * 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, then an iteration is performed as described in 5.5 or 5.5.2, and for each value of the loop parameter of the iteration, the Add_Unnamed procedure is invoked, with the anonymous object A as the first parameter and the result of evaluating the expression as the second parameter. Note that there is only one indented bullet here (there are plenty of the outer bullets). The ISO drafting rules don't allow such things, and it doesn't make a ton of sense in the first place. Is there some way to rewrite this so that it makes sense either as multiple subbullets or no subbullets at all? My best attempts are something like: * for any other named_container_aggregate, the container_element_associations (which are necessarily iterated_element_associations) are evaluated in the order given; for each such association: * the iterated_element_association is first elaborated; * then an iteration is performed as described in 5.5 or 5.5.2, and for each value of the loop parameter of the iteration, the Add_Unnamed procedure is invoked, with the anonymous object A as the first parameter and the result of evaluating the expression as the second parameter. Or * for any other named_container_aggregate, the container_element_associations (which are necessarily iterated_element_associations) are evaluated in the order given; for each such association, the iterated_element_association is elaborated, then an iteration is performed as described in 5.5 or 5.5.2, and for each value of the loop parameter of the iteration, the Add_Unnamed procedure is invoked, with the anonymous object A as the first parameter and the result of evaluating the expression as the second parameter. The latter is a bit of a run-on paragraph, and the former is ordering bullets (which we're really not supposed to do, either, although we do it often). Note: I didn't put any periods in the text as the entire block is written without any. Just doing that in one bullet would be unusual. **************************************************************** From: Tucker Taft Sent: Saturday, January 26, 2019 6:22 PM How about this slight rewording of your first attempt: * for any other named_container_aggregate, the container_element_associations (which are necessarily iterated_element_associations) are evaluated in the order given; each such evaluation comprises two steps: 1. the iterated_element_association is elaborated; 2. an iteration is performed as described in 5.5 or 5.5.2, and for each value of the loop parameter of the iteration, the Add_Unnamed procedure is invoked, with the anonymous object A as the first parameter and the result of evaluating the expression as the second parameter. **************************************************************** From: Randy Brukardt Sent: Sunday, January 27, 2019 3:48 PM Love it! I'll use that as your Editorial Review comment on this AI (so we don't have to see this again). **************************************************************** From: Randy Brukardt Sent: Thursday, January 24, 2019 7:47 PM The rules for setting the actual parameter to a call of the Empty function for a container_aggregate end with: * otherwise, to an implementation-defined value. The requirements for the Empty function are: ... or denote a function with a result type of @i that has no parameters, or that has one @b parameter of type Integer with a default expression. So we've required the function to have a default expression, but nowhere do we ever use that default for anything. So, it seems to me that we should either drop the requirement for a default expression, or use the default expression in the "otherwise" case. I'm not sure which is better, so I ask for thoughts. (Dropping the requirement is probably a bit less change and bit easier for me, but that's hardly a reason to do something.) **************************************************************** From: Tucker Taft Sent: Saturday, January 26, 2019 6:33 PM ... > So, it seems to me that we should either drop the requirement for a > default expression, or use the default expression in the "otherwise" case. I believe the original intent was that simple-minded implementations would use the default, while more sophisticated implementations might try to estimate the number of elements. But you are right that that is pretty hard to enforce! Perhaps drop the requirement, but add the following implementation advice: Implementation Advice The implementation-defined value supplied to an Empty function with one formal parameter should use the default for that parameter, if any, in the absence of an estimate for the number of elements in the aggregate. If there is no default, the implementation should use zero when it has no estimate. **************************************************************** From: Randy Brukardt Sent: Sunday, January 27, 2019 3:57 PM Humm. I suppose I can process dropping the requirement as an Editorial Review comment (getting rid of unused rules seems to qualify). Can't add an ImplAdv that way, though. In any case, this ImplAdv seems like overkill to me. It's a lot of words that say "do something reasonable". Perhaps this would make more sense as an AARM Implementation Note, which we could place directly after the "otherwise, to an implementation-defined value" (so we don't need all of the setup text). AARM Implementation Note: This value ought to be an estimate for the number of elements in the aggregate, if one is available. If not, it is suggested to use the default value for the parameter if one exists, and zero otherwise. P.S. We have a combined Winter Storm Warning (6-10 inches) and Wind-Chill Warning (-35F) for tomorrow, so I'm working a half day today in hopes of not totally losing the work day. (And am doing today's wash tomorrow, so I won't come in until late regardless of what the weather does.) **************************************************************** From: Tucker Taft Sent: Sunday, January 27, 2019 5:22 PM >AARM Implementation Note: > This value ought to be an estimate for the number of > elements in the aggregate, if one is available. If not, > it is suggested to use the default value for the parameter > if one exists, and zero otherwise. Works for me! >P.S. We have a combined Winter Storm Warning (6-10 inches) and Wind-Chill >Warning (-35F) for tomorrow, so I'm working a half day today in hopes of not >totally losing the work day. (And am doing today's wash tomorrow, so I won't >come in until late regardless of what the weather does.) Brrrrrrrrrr! **************************************************************** From: Randy Brukardt Sent: Thursday, January 24, 2019 4:58 PM The resolution rules for the Aggregate aspect include: In the case of Assign_Indexed, the key type shall be the same type as that of the parameters of New_Indexed. and If both Add_Unnamed and Assign_Indexed are specified, the final parameters shall be of the same type @emdash the element type of @i. These two rules mean that one has to resolve all four subprogram names for the Aggregate aspect as a group, rather than each individually. While that's possible, it seems overly complicated and nothing like any other existing resolution. I would think it would make more sense to resolve each name individually and then apply cross-item rules as Legality Rules. This is how existing aspects work (although none use an aggregate template to specify all of the names at once). This could make a difference if, say, both Add_Unnamed and Assign_Indexed are specified and the given names are overloaded. For instance: type Cont is ... with Aggregate => Add_Unnamed => Append, Assign_Indexed => Replace_Element); procedure Append (C : in out Cont; E : Element_Type); procedure Append (C : in out Cont; X : Other_Type); procedure Replace_Element (C : in out Cont; K : Key_Type; E : Element_Type); As resolution rules, the aspect would be legal, as Legality Rules, the aspect would be illegal (as there would be no way to resolve the two Appends - they both have the required profile). In the latter case, one could always use a rename to avoid the problem without adding an runtime cost. I note that there isn't a Legality Rules section for the aspect definition. There is a paragraph of rules in the primary aspect text that really ought to be listed under Legality Rules as well: A /procedure_/name shall be specified for at least one of Add_Named, Add_Unnamed, or Assign_Indexed. If Add_Named is specified, neither Add_Unnamed nor Assign_Indexed shall be specified. Either both or neither of New_Indexed and Assign_Indexed shall be specified. If the associated type T is a private type, the full type shall not be an array type. If type T is limited, the name specified for Empty shall denote a function rather than a constant object. Argubly the first part are more syntax rules which could reasonably stay with the syntax definition (English-language syntax rules being a long tradition in the RM, and those are essentially the same as Legality Rules except that they are specifically about syntax. But the two rules about types really are Legality Rules that have nothing to do with the syntax. So I'd suggest making a Legality Rules section that follows the Name Resolution Rules: Legality Rules If the associated type T is a private type, the full type shall not be an array type. If type T is limited, the name specified for Empty shall denote a function rather than a constant object. The key type of Assign_Indexed shall be the same type as that of the parameters of New_Indexed. If both Add_Unnamed and Assign_Indexed are specified, the final parameters shall be of the same type -- the element type of T. I could make this change as an Editorial Review change, but I'd like some feedback on whether it is important to use the unusually complex resolution rules for the aspect. Aside: "T" is used throughout the syntax, name resolution, and legality rules for this section. This is highly unusual, especially once the rules in question are in a different heading section in the RM. I wonder if we should consider giving this type a name, say the "container type", and using that rather than a "T" defined seven paragraphs prior. It would make the paragraphs more wordy, but easier to understand when jumping into the middle of the rules. That would look something like for the Legality Rules: If the container type of the Aggregate aspect is a private type, the full type of the container type shall not be an array type. If container type is limited, the name specified for Empty shall denote a function rather than a constant object. The key type of Assign_Indexed shall be the same type as that of the parameters of New_Indexed. If both Add_Unnamed and Assign_Indexed are specified, the final parameters shall be of the same type -- the element type of the container type. Thoughts?? **************************************************************** From: Randy Brukardt Sent: Sunday, January 27, 2019 6:12 PM Any thoughts on the {above}? In the absence of comments, I'll probably write something as part of a fix-up AI (I won't make a change this significant unilaterally). **************************************************************** From: Tucker Taft Sent: Sunday, January 27, 2019 7:02 PM > These two rules mean that one has to resolve all four subprogram > names for the Aggregate aspect as a group, rather than each > individually. While that's possible, it seems overly complicated and > nothing like any other existing resolution. I would think it would > make more sense to resolve each name individually and then apply > cross-item rules as Legality Rules. This is how existing aspects work > (although none use an aggregate template to specify all of the names > at once). Agreed, that sounds better. Introducing new kinds of name-resolution rules is a pain for all. ... > I could make this change as an Editorial Review change, but I'd like > some feedback on whether it is important to use the unusually complex > resolution rules for the aspect. No, simplify the resolution rules as you do this. > That would look something like for the Legality Rules: > > If the container type of the Aggregate aspect is a private type, > the full > type of the container type shall not be an array type. If > container type is > limited, the name specified for Empty shall denote a function > rather than a > constant object. > > The key type of Assign_Indexed shall be the same type as that of the > parameters of New_Indexed. If both Add_Unnamed and Assign_Indexed > are specified, > the final parameters shall be of the same type -- the element type > of the > container type. > > Thoughts?? Agree, better to switch to "the container type" once you get too far from the original aspect syntax. ****************************************************************