AI22-0158-1
!standard 4.4(2) 26-02-10 AI22-0158-1/01
!class Amendment 26-02-10
!status work item 26-02-10
!status received 25-08-19
!assigned author Tucker Taft
!submitter Tucker Taft
!priority Low
!difficulty Easy
!subject Range equivalent to two-component record aggregate
The ".." operation used in defining a range is treated as equivalent to a two-component record aggregate if it is used where an expression is expected. The expected type shall be a single record type with exactly two components.
There are places where it would be natural to define a subprogram that took a range as a parameter, such as a function that returned a slice of a vector. Currently the syntax for a range uses a notation that the user has no ability to connect to a user-defined operation.
It would be useful to provide a way for this notation to be used as a parameter to a user-defined subprogram.
A range appearing where an expression is expected is interpreted as equivalent to a two-component record aggregate, requiring a single expected type that is a record with two components. That is, we propose to allow ranges any place that an expression is permitted, interpreting A .. B as equivalent to (A, B) and M'Range as equivalent to (M'First, M'Last).
Replace 4.4(2):
expression ::=
relation {and relation} | relation {and then relation}
| relation {or relation} | relation {or else relation}
| relation {xor relation}
with:
expression ::=
relation {and relation} | relation {and then relation}
| relation {or relation} | relation {or else relation}
| relation {xor relation}
| range
Further wording is TBD.
Ada's range notation, of A .. B, could be useful for a variety of operations, such as a function that returns a slice of a vector, or an iterator that iterates over a range of integers. It could also be used as a way of defining an upper and lower bound of a constraint of some sort on an abstract type, such as a range of Big Integers or Big Reals, or the first and last String associated with some part of a dictionary.
The simplest way of allowing this notation to be useful for abstract types seems to interpret it as a two-component record aggregate. We considered generalizing this to be usable with two-component arrays as well as records, but this could lead to ranges unintentionally appearing in places where they might make no sense, but might nevertheless happen to be semantically legal, potentially confusing the reader. So as an initial step we suggest requiring the expected type be a record type with exactly two components. This would seem to minimize the chance of unintentional use.
An alternative approach would be to have an aspect (say, Is_Range) that could only be applied to two component record types which would enable such aggregates. That would avoid the notation from being used on unexpected types, but otherwise would just be noise.[a]
type Bounds is record
Low : Positive;
High : Natural;
end record;
type Vec is private
with Constant_Indexing => Element;
function Element(V : Vec; B : Bounds) return Vec;
-- Return a slice of V
...
X : Vec := ...
Y : Vec := X(2 .. 5); -- Invokes "Element" implicitly
-- X(2 .. 5) is equivalent to Element (X, (2, 5))
An ACATS B-Test is needed that verifies that the expected type must be a single, two-component, record type when the range notation is used in expression context. An ACATS C-Test is needed to check that the notation is supported.
This addresses ARG GitHub issue #142 and addresses ARG GitHub issue #141 for reading of slices. (Writing of slices is problematic for reasons beyond just describing ranges.)
[a]I slightly prefer this approach because I don't want to see certain Claw types written as ranges:
type Size_Type is record
Height, Width : Claw.Int;
end record;
(Position is similar.) A call like:
Create (Window, Size => 10..20, Position => 3..3, ...);
would be legal with the original proposed rules, but hardly sensible.