AI22-0134-1
!standard 4.3.5(5/5) 25-08-15 AI22-0134-1/03
!standard 4.3.5(11/6)
!standard 4.3.5(27/5)
!standard 4.3.5(45/5)
!class Amendment 25-05-21
!status Revision-202Y 25-05-30
!status WG9 Approved 25-10-08
!status ARG Approved 13-0-1 25-05-28
!status work item 25-05-21
!status received 24-06-27
!assigned author Tucker Taft
!submitter Tucker Taft
!priority Medium
!difficulty Medium
!subject Allow Both Add_Named and Add_Unnamed in Aggregate aspect
Allow both Add_Named and Add_Unnamed within a single Aggregate aspect, in order to better support heterogeneous objects such as JSON objects.
Currently if there is an Add_Named procedure specified in an Aggregate aspect, Add_Unnamed is not permitted (see RM 4.3.5(5/5)). This seems like an unnecessary restriction. In particular, the only ambiguity is in an aggregate of the form:
[for I in 1 .. 10 => expr]
whether Add_Named or Add_Unnamed would be used. It is pretty easy to argue that Add_Unnamed should be used here, and Add_Named would only be used if there were an explicit "use Key" present, as in:
[for I in 1 .. 10 use Key_Tab(I) => Val_Tab(I)]
By allowing both Add_Named and Add_Unnamed, it would be easier to define a type representing a heterogeneous structure like a JSON object. Add_Named would be used to create a map-like object, while Add_Unnamed would be used to create an array-like object.
Should this restriction be lifted? (Yes.)
Allow both Add_Named and Add_Unnamed in the same Aggregate aspect. For any given container aggregate, Add_Unnamed would be used in all cases where both are currently permitted. Add_Named would be used only when Add_Unnamed is not permitted.
In particular, if there is no explicit key_expression associated with an iterated_element_association, Add_Unnamed would be used.
Modify 4.3.5(5/5):
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 {not }be specified. Either both or neither of New_Indexed and Assign_Indexed shall be specified.
Modify 4.3.5(11/6):
For an Aggregate aspect, neither the element type nor the key type (if any) of the container type shall be a limited type. Additionally, the key type of Assign_Indexed shall be the same type as that of the parameters of New_Indexed, and that type shall be a discrete type. If both Add_Unnamed and {either Add_Named or }Assign_Indexed are specified, the final parameters shall be of the same type — the element type of the container type.
Modify 4.3.5(21.a/6):
Discussion: The capabilities supported for aggregates of a particular container type depend on the subprograms specified in the Aggregate aspect. If only Add_Unnamed is provided in the Aggregate aspect, then container aggregates only support capabilities relevant for a container that holds a set or sequence of elements. If only Add_Named is provided, then container aggregates support capabilities relevant for a map container. If Assign_Indexed is provided, then container aggregates support capabilities relevant for a container that is indexed by a contiguous discrete range of values. [Finally, if]{If} both Add_Unnamed and Assign_Indexed are provided, then container aggregates support capabilities similar to those provided for array aggregates (excepting others choices).{ Finally, if both Add_Named and Add_Unnamed are provided, then container aggregates support capabilities relevant for a heterogeneous structure that can hold either a map or sequence of elements.} Specifically:
Add column to 4.3.5(21.a.6):
|
Capability |
Both Add_Named & Add_Unnamed |
|
Null aggregates |
Yes |
|
Positional aggregates |
Yes |
|
Named aggregates: |
|
|
key type |
Any nonlimited |
|
<> allowed |
No |
|
choice completeness required |
No |
|
iterators can be used with other kinds of container_element_associations in a single aggregate |
Yes |
|
with a key_choice being an expression |
Yes |
|
with a key_choice being a discrete_range |
Discrete key type only1 |
|
with an iterator with a key_expression |
Yes |
|
with an iterator with a filter |
Yes |
|
with an iterator with a discrete range (in) and no key_expression |
Yes |
|
with an array iterator (of) and no key_expression |
Yes |
|
with a cursor iterator (in) and no key_expression |
Yes |
|
with a container iterator (of) and no key_expression |
Yes |
Modify 4.3.5(27/5):
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{ but no Add_Unnamed} 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.{ Furthermore, if both Add_Named and Add_Unnamed are specified, but one or more of the container_element_associations has a key_choice_list or a key_expression, then the above restriction applies to every iterated_element_association without a key_expression.}
Modify 4.3.5(27.a/5):
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{ in the absence of an Add_Unnamed procedure}.
Modify 4.3.5(45/5):
There are data structures that can hold either a map-like structure or a vector-like structure. A JSON object is one such example, as might be an element of an XML or HTML DOM. For such data structures, it is useful for the form of the container aggregate to determine which kind of structure is contained. By allowing both an Add_Named and Add_Unnamed procedure in a single Aggregate aspect, we can support such heterogeneous containers.
When it might make sense to use either Add_Named or Add_Unnamed, we specify the use of Add_Unnamed. These are cases where there is no explicit specification of a key, and Add_Unnamed is generally less restrictive.
Note that, based on the ordering of rules in the Dynamic Semantics section, if Add_Named is used for any container_element_association, it is used for all of them. This is also checked by 4.3.5(27/5).
A JSON object is a heterogeneous data structure that can hold various different kinds of data, including both sequences and dictionaries. Depending on whether Add_Named or Add_Unnamed is used, the result will either be a dictionary or a sequence. Based on 4.3.5(27/5) and 4.3.5(45/5), Add_Named will be used throughout a given aggregate if it is used anywhere, so there will never be "mixed" aggregates.
type JSON_Object is private
with Aggregate => (Empty =>
Empty_Object,
Add_Unnamed => Append,
Add_Named
=> Add_To_Dict)
String_Literal => JSON_String,
Integer_Literal => JSON_Integer;
function Empty_Object return JSON_Object;
procedure Append (Seq : in out JSON_Object;
Elem : in
JSON_Object);
procedure Add_To_Dict(Dict : in out JSON_Object;
Key
: in String;
Elem :
in JSON_Object);
function JSON_String (Str : Wide_Wide_String) return JSON_Object;
function JSON_Integer (Val : String) return JSON_Object;
...
Two_Presidents : constant JSON_Object :=
[ ["first" => "George", "last" =>
"Washington",
"address" => ["city" => "Mount
Vernon", "state" => "Virginia"],
"terms" => [1789, 1793]],
["first" => "John", "last" =>
"Adams",
"address" => ["city" =>
"Quincy", "state" => "Massachusetts"],
"terms" => [1797]] ];
-- The above aggregate involves calls on Append for the outermost
-- list of presidents, and for the list of terms, and Add_To_Dict
-- for the other aggregates, while calling JSON_String and JSON_Integer
-- for the string and integer literals, respectively (except for
-- the keys for the dictionary components).
@drepl
The type for which this aspect is specified is known as the @i{container type} of the Aggregate aspect. A @i{procedure_}@fa{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.
@dby
The type for which this aspect is specified is known as the @i{container type} of the Aggregate aspect. A @i{procedure_}@fa{name} shall be specified for at least one of Add_Named, Add_Unnamed, or Assign_Indexed. If Add_Named is specified, Assign_Indexed shall not be specified. Either both or neither of New_Indexed and Assign_Indexed shall be specified.
@drepl
For an Aggregate aspect, neither the element type nor the key type (if any) of the container type shall be a limited type. Additionally, the key type of Assign_Indexed shall be the same type as that of the parameters of New_Indexed, and that type shall be a discrete type. 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.
@dby
For an Aggregate aspect, neither the element type nor the key type (if any) of the container type shall be a limited type. Additionally, the key type of Assign_Indexed shall be the same type as that of the parameters of New_Indexed, and that type shall be a discrete type. If both Add_Unnamed and either Add_Named or Assign_Indexed are specified, the final parameters shall be of the same type @emdash the element type of the container type.
@drepl
For an @fa{iterated_element_association} without a @i{key_}@fa{expression}, if the @fa{aggregate} is an indexed aggregate or the expected type of the @fa{aggregate} specifies an Add_Named procedure in its Aggregate aspect, then the type of the loop parameter of the @fa{iterated_element_association} shall be the same as the key type of the @fa{aggregate}.
@dby
For an @fa{iterated_element_association} without a @i{key_}@fa{expression}, if the @fa{aggregate} is an indexed aggregate, or the expected type of the @fa{aggregate} specifies an Add_Named but no Add_Unnamed procedure in its Aggregate aspect, then the type of the loop parameter of the @fa{iterated_element_association} shall be the same as the key type of the @fa{aggregate}. Furthermore, if both Add_Named and Add_Unnamed are specified, but one or more of the @fa{container_element_association}s has a @fa{key_choice_list} or a @i{key_}@fa{expression}, then the above restriction applies to every @fa{iterated_element_association} without a @i{key_}@fa{expression}.
@drepl
@xbullet{for a @fa{named_container_aggregate} for a type with an Add_Named procedure in its Aggregate aspect, the @fa{container_element_association}s are evaluated in an arbitrary order:}
@dby
@xbullet{for a @fa{named_container_aggregate} for a type with an Add_Named procedure in its Aggregate aspect, and either no Add_Unnamed procedure, or the @fa{aggregate} has a @fa{container_element_association} with a @fa{key_choice_list} or a @i{key_}@fa{expression}, the @fa{container_element_association}s are evaluated in an arbitrary order:}
This is a new combination, so a new ACATS test with this combination should be created. A B-test with a "mixed" aggregate should be created, testing 4.3.5(27/5).
This is based on ARG GitHub issue #100.