Version 1.13 of ai12s/ai12-0125-3.txt

Unformatted version of ai12s/ai12-0125-3.txt version 1.13
Other versions for file ai12s/ai12-0125-3.txt

!standard 5.2.1(0)          16-10-02 AI12-0125-3/08
!standard 2.2(9)
!standard 3.3(21.1/3)
!standard 4.1(2/3)
!standard 8.6(9/4)
!class Amendment 15-10-13
!status Amendment 1-2012 16-08-04
!status ARG Approved 9-0-1 16-10-10
!status work item 16-10-08
!status ARG Approved 10-0-2 16-06-12
!status work item 15-10-13
!status received 15-09-22
!priority Low
!difficulty Medium
!subject Add @ as an abbreviation for the LHS of an assignment
!summary
Define @, which can be used in the right-hand side of an assignment statement as an abbreviation for the name of the left-hand side.
!problem
Incrementing, decrementing, scaling, etc., are all somewhat painful in Ada, particularly if the object being updated has a long name, or involves any sort of dynamic addressing:
My_Package.My_Array(I).Field := My_Package.My_Array(I).Field + 1;
Some sort of shorthand would be welcome, as it would ease both readability and writability, while reducing the possibility of error, such as unintended multiple evaluations of expressions in the name, or mismatching array indices.
!proposal
We define a new symbol, @, the target name which represents a constant view of the left-hand side of an assignment.
!wording
Add @ to the list in 2.2(9):
& ' ( ) * + , – . / : ; < = > @ |
Add after 3.3(21.1/3): [Editor's note: This is the list of items that are defined to be constant. Since that list is defined to be exclusive, we have to include target_name here.]
* a target_name of an assignment_statement when used in the expression of the assignment (see 5.2.1);
[Editor's note: The "when used in the expression of the assignment" isn't strictly necessary, as that is the only place a target_name is allowed. But this is a forward reference and a casual reader might interpret this as applying to the variable_name of the assignment -- which it does not.]
Add target_name to the syntax of Name in 4.1(2/3).
[Editor's note: We add a new syntax nonterminal because all of the other kinds of name are defined that way, rather than by reference to other subclauses as is done for a primary. We try to be consistent within a clause, even if the Standard is not consistent for similar constructs.]
Add a new subclause:
5.2.1 Target Name Symbols
@, known as the target name of an assignment statement, provides an abbreviation to avoid repetition of potentially long names in assignment statements.
Syntax
target_name ::= @
Name Resolution Rules
Redundant[If a target_name occurs in an assignment_statement A, the variable_name V of A is a complete context. The target name is a constant view of V, having the nominal subtype of V.]
AARM The Proof: The complete context rule is formally given in 8.6. The constant view rule is formally given in 3.3; the nominal subtype follows from the equivalence given below in Static Semantics.
Legality Rules
A target_name shall only appear in the expression of an assignment_statement.
Static Semantics
@Redundant[The variable_name is evaluated only once.] In particular, if a target_name with nominal subtype S appears in the expression of an assignment statement A, then A is equivalent to a call on a local anonymous procedure with the actual parameter being the variable_name of A, where the local anonymous procedure has an in out parameter with unique name P of subtype S, with a body being A with the variable_name being replaced by P, and any target_names being replaced by the qualified expression S'(P).
[Editor's note: We use a qualified expression here to ensure the replacement has the semantics of a constant view. I would have preferred to avoid that (a target_name is a constant view by rule), but just using P here is misleading, especially if someone is doing expression analysis after this expansion. We could probably drop the explicit definition of @ as a constant view, since this expansion would have that effect anyway, but it seems valuable to emphasize that to readers.]
AARM Reason: This equivalence defines all of the Dynamic Semantics for these assignment statements.
AARM Discussion:
For example, the expression
My_Array(I) := @*2 + @/2;
would be equivalent to
declare procedure [Anon] ([Target] : in out [Target_Subtype]) is begin [Target] := [Target_Subtype]'([Target])*2 + [Target_Subtype]'([Target])/2; end [Anon];
begin [Anon] ([Target] => My_Array(I)); end;
where all of the identifiers in square brackets are anonymous placeholders. End AARM Discussion.
Examples
Board(1, 1) := @ + 1; -- An abbreviation for Board(1, 1) := Board(1, 1) + 1;
-- (Board is declared in 3.6.1)
My_Complex_Array : array (1 .. Max) of Complex; -- See 3.3.2, 3.8. ... -- Square the element in the Count (see 3.3.1) position: My_Complex_Array (Count) := (Re => @.Re**2 - @.Im**2, Im => 2.0 * @.Re * @.Im); -- A target_name can be used multiple times and as a prefix if needed.
Add after 8.6(9/4):
* The *variable_*name of an assignment_statement *A*, if the expression of A contains one or more target_names.
!examples
X := @ + 1; -- An abbreviation for X := X + 1;
A(I) := Integer'Max (@, 10); -- Equivalent to: -- declare -- procedure Anon (Target : in out Integer) is -- begin -- Target := Integer'Max (Integer'(Target), 10); -- end Anon; -- begin -- Anon (A(I)); -- end;
-- If one has a function Clamp (Low, Value, High : S) return S that ensures -- that Value is in the range Low .. High, then one could write:
Something_Long(1).Comp := Clamp (L, @ + X, U);
-- The preceeding two examples cannot be written using AI12-0125-2, -- operations like :+ have limited applicability.
The following example is some real code from the web log analyzer that makes usage reports for Ada-Auth.org and Archive.AdaIC.com (among others). The value of this proposal in reducing clutter is more evident in a real example. First, the original Ada 2012 code (41 lines):
procedure Record_Amount (Data : in out Item_Data; Month : in Month_Number; Year : in Report_Year_Number; Amount : in Item_Count := 1; Is_Scanner : in Boolean := False; Is_View : in Boolean := True) is begin if Data.By_Month (Year, Quarter_Num (Month)) = Empty_Quarter_Item_Data then -- We need to allocate an item. Last_Allocated_Quarter_Item_Data := Last_Allocated_Quarter_Item_Data + 1; Data.By_Month (Year, Quarter_Num (Month)) := Last_Allocated_Quarter_Item_Data; end if; Data.Total.Hit_Count := Data.Total.Hit_Count + Amount; Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).Hit_Count := Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).Hit_Count + Amount; if not Is_Scanner then Data.Total.Non_Scanner_Hit_Count := Data.Total.Non_Scanner_Hit_Count + Amount; Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).Non_Scanner_Hit_Count := Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).Non_Scanner_Hit_Count + Amount; end if; if Is_View then Data.Total.View_Count := Data.Total.View_Count + Amount; Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).View_Count := Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).View_Count + Amount; if not Is_Scanner then Data.Total.Non_Scanner_View_Count := Data.Total.Non_Scanner_View_Count + Amount; Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).Non_Scanner_View_Count := Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).Non_Scanner_View_Count + Amount; end if; end if; end Record_Amount;
Here is the same example using the @ abbreviation (33 lines):
procedure Record_Amount (Data : in out Item_Data; Month : in Month_Number; Year : in Report_Year_Number; Amount : in Item_Count := 1; Is_Scanner : in Boolean := False; Is_View : in Boolean := True) is begin if Data.By_Month (Year, Quarter_Num (Month)) = Empty_Quarter_Item_Data then -- We need to allocate an item. Last_Allocated_Quarter_Item_Data := @ + 1; Data.By_Month (Year, Quarter_Num (Month)) := Last_Allocated_Quarter_Item_Data; end if; Data.Total.Hit_Count := @ + Amount; Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).Hit_Count := @ + Amount; if not Is_Scanner then Data.Total.Non_Scanner_Hit_Count := @ + Amount; Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).Non_Scanner_Hit_Count := @ + Amount; end if; if Is_View then Data.Total.View_Count := @ + Amount; Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).View_Count := @ + Amount; if not Is_Scanner then Data.Total.Non_Scanner_View_Count := @ + Amount; Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month))) (Quarter_Item (Month)).Non_Scanner_View_Count := @ + Amount; end if; end if; end Record_Amount;
!discussion
The occurrence of a target_name (@) in an assignment makes the /variable_/name (the left-hand side of the assignment) a complete context. This avoids complicating resolution unnecessarily; otherwise it would be possible for the LHS to be overloaded in such a way that the uses of @ determine which possibility is correct. This is especially complicated as @ has to resolve the same as the LHS, whereas a simple textual substitution would not have that property. We avoid the complication by legislating it away.
This is potentially a maintenance hazard, as introducing @ into an assignment_statement potentially could make it illegal. However, since stand-alone objects and parameters cannot be overloaded, this will rarely make a difference in practice (there is rarely more than one interpretation of the LHS of an assignment).
The expansion in terms of an anonymous procedure preserves the possibility of erroneous execution in obscure cases:
type Mutable (D : Boolean := True) is record case D is when True => I : Integer; when False => F : Float; end case; end record;
Obj : Mutable;
function Fooey return Integer is Temp : Integer := Obj.I; begin Obj := (D => False, F => Float(Temp)); return Temp; end Fooey;
Obj.I := @ + Fooey; -- (1) Obj.I := Obj.I + Fooey; -- (2)
In these assignments, the I component doesn't exist when it is written. Ada doesn't try to prevent that, both (1) and (2) are erroneous.
We considered using a renames formulation instead:
Obj.I := @ + 1;
being the same as:
declare <Target> : renames Integer := Obj.I; -- (3) begin <Target> := <Target> + 1; end;
However, this doesn't work in this case, as the renames of a discriminant dependent-component (like (3)) is illegal. Since the update to the I component is the sort of use this feature is constructed for, making it illegal seems too restrictive. (And this is not a new kind of erroneous execution, as demonstrated previously).
Note that the subprogram call introduced by this formulation could introduce an extra possibility of a failing subtype check, but this can happen only if the value of the variable_name is invalid or a predicate has a side-effect. In these cases, reading the variable_name isn't going to be safe anyway, so the effect is mainly to make a latent error more visible.
The target_name is a constant view of the LHS of the assignment. We do this mainly so that the use of @ does not trigger the Taft anti-aliasing rules (6.4.1). A side-effect is that the target object cannot be modified directly via @, preventing problems. We accomplish this by defining the target_name to expand to a qualified_expression; that expression could introduce an extra exception, but only if a predicate has a side-effect (the subtype conversion was already done by the parameter passing, this is just a repeat).
This proposal is better than AI12-0125-2, as
(1) It is more flexible; it can be used in function calls other than operators
(as shown by the 'Max and Clamp examples above).
(2) There is a positive indication that this notation is being used (the
addition of an @ as opposed to deleting an '=');
(3) It doesn't (directly) depend on visibility of anything; it's always
available unless the LHS of the assignment is overloaded.
It is different than the proposal of AI12-0125-2: (whether these are advantages or disadvantages are in the eye of the beholder -- the author finds these to be advantages)
(1) Only one new lexical symbol is needed, rather than many. (2) It doesn't have an "obvious" extension to a user-defined version, which
avoids a nasty slippery slope.
(3) It doesn't look like we're out of ideas by stealing groddy parts of other
languages.
This proposal is the same as the proposal AI12-0125-2 in the following ways:
(1) The semantics is described in terms of an anonymous procedure. (2) The left-hand side of the assignment is treated as a complete context,
meaning that in rare cases, introducing @ into an assignment (or using :+) would make the assignment illegal if the LHS was ambiguous.
These should be considered issues with solving the problem as opposed to negatives against a particular proposal.
(3) Both are only one character different from very different expressions:
Obj:=@+1; vs. Obj:=+1; -- This proposal Obj:+1; vs. Obj:=+1; -- The alternative AI12-0125-2
Of course, writing without whitespace is discouraged in Ada, but these are legal expressions regardless of common practice. This problem could have been eliminated for this proposal by using more than one character for the target name (as the original proposal <<>> did), but that would also make it harder to write the abbreviation, reducing its utility.
!corrigendum 2.2(9)
Replace the paragraph:
&    '    (    )    *    +    ,    –    .    /    :    ;    <    =    >    |
by:
&    '    (    )    *    +    ,    –    .    /    :    ;    <    =    >    @    |
!corrigendum 3.3(21.1/3)
Insert after the paragraph:
the new paragraph:
!corrigendum 4.1(2/3)
Replace the paragraph:
name ::= direct_name | explicit_dereference | indexed_component | slice | selected_component | attribute_reference | type_conversion | function_call | character_literal | qualified_expression | generalized_reference | generalized_indexing
by:
name ::= direct_name | explicit_dereference | indexed_component | slice | selected_component | attribute_reference | type_conversion | function_call | character_literal | qualified_expression | generalized_reference | generalized_indexing | target_name
!corrigendum 5.2.1(0)
Insert new clause:
@, known as the target name of an assignment statement, provides an abbreviation to avoid repetition of potentially long names in assignment statements.
Syntax
target_name ::= @
Name Resolution Rules
If a target_name occurs in an assignment_statement A, the variable_name V of A is a complete context. The target name is a constant view of V, having the nominal subtype of V.
Legality Rules
A target_name shall only appear in the expression of an assignment_statement.
Static Semantics
The variable_name is evaluated only once. In particular, if a target_name with nominal subtype S appears in the expression of an assignment_statement A, then A is equivalent to a call on a local anonymous procedure with the actual parameter being the variable_name of A, where the local anonymous procedure has an in out parameter with unique name P of subtype S, with a body being A with the variable_name being replaced by P, and any target_names being replaced by the qualified expression S'(P).
Examples
Board(1, 1) := @ + 1; -- An abbreviation for Board(1, 1) := Board(1, 1) + 1; -- (Board is declared in 3.6.1).
My_Complex_Array : array (1 .. Max) of Complex; -- See 3.3.2, 3.8. ... -- Square the element in the Count (see 3.3.1) position: My_Complex_Array (Count) := (Re => @.Re**2 - @.Im**2, Im => 2.0 * @.Re * @.Im); -- A target_name can be used multiple times and as a prefix if needed.
!corrigendum 8.6(9/4)
Insert after the paragraph:
the new paragraph:
!ASIS
Some query is needed, details to follow.
!ACATS Tests
ACATS B and C-Tests would be needed.
!appendix

From: Randy Brukardt
Sent: Friday, January 8, 2016  6:52 PM

I've been revising AI12-0125-3, "@ as a shorthand for the LHS of an
assignment", as I was tasked to do during the Bennington meeting. (I'll post
that later this evening, so you can look at it if you want.)

I'm thinking about how that proposal (indeed, all of the proposals) deal
with the two major objections to it:

(1) Easy to lose the special case in a complex RHS expression. Even though @
is a "big" character, it's still only one character. It does well in the
normal case:
      Obj.I := @ + 1;
so we didn't worry about this too much.

(2) A one character difference can transform an expression into something
else legal that means something very different.

This originally was raised with the proposal of AI12-0125-2:
     Obj:+1;     vs. Obj:=+1;

But AI12-0125-3 has the same problem:
     Obj:=@+1;   vs. Obj:=+1;

I tried for quite a while to come up with a reason why the latter is less of
a problem than the former, and I've sadly concluded that the only reason
that's true is because I want it to be so. :-)

That led me to think about alternatives that would avoid the second problem,
and I realized that there is a idea that avoids the first problem as well,
with a bit of extra verbosity. So I'll outline it here so it doesn't get
completely lost in the mists of time.

-------------

The basic idea is to resurrect the use of square brackets for this purpose
(as was once proposed by someone). But rather than just using [] to
represent the target_name, we also surround the actual target name in square
brackets as well:

   [X] := [] + 1;  --  A shorthand for X := X + 1;

   [A(I)] := Integer'Max ([], 10); -- A shorthand for A(I) := Integer'Max
(A(I), 10);

The square brackets around the LHS keys the reader that (A) this is an
assignment with a shorthand; (B) the text in brackets is a complete context.
Thus the use of the target_name shorthand does not harm readability in that
way.

I think the mental model of this feature would be a bit easier to think
about as well: one would think of the target_name [] as being a placeholder
for the contents of the square brackets on the LHS. That's not 100%
accurate, but it would be close enough for almost all uses.

The actual semantics would be exactly the same as proposed in AI12-0125-3
for @. One could imagine an extension to the idea where more or less
arbitrary names or expressions could be repeated this way, but I am not
trying to propose that.

The next message will have an extended example of this idea, and others, in
a more realistic situation.

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

From: Randy Brukardt
Sent: Friday, January 8, 2016  7:16 PM

One of the problems that we've had evaluating these proposals is that the
examples tend to be rather silly, where the repeated names are not a problem
for readability. In order to give us something more realistic to chew on,
following is a routine that forms part of the log analyzer that I created to
make web site reports for Ada-Auth.Org, Archive.AdaIC.com, and others.
(Originally it was used on AdaIC.org as well, but not anymore.) [Note: I
removed a renames, but otherwise this is the actual code, not a constructed
example.] Note that this example doesn't need the flexibility proposed by
some of the alternatives, so I can present how it looks 4 ways.


Ada 2012 version (41 lines):

    procedure Record_Amount (Data : in out Item_Data;
                             Month : in Month_Number;
                             Year : in Report_Year_Number;
                             Amount : in Item_Count := 1;
                             Is_Scanner : in Boolean := False;
                             Is_View : in Boolean := True) is
    begin
        if Data.By_Month (Year, Quarter_Num (Month)) = Empty_Quarter_Item_Data then
            -- We need to allocate an item.
            Last_Allocated_Quarter_Item_Data := Last_Allocated_Quarter_Item_Data + 1;
            Data.By_Month (Year, Quarter_Num (Month)) :=
               Last_Allocated_Quarter_Item_Data;
        end if;
        Data.Total.Hit_Count := Data.Total.Hit_Count + Amount;
        Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
             (Quarter_Item (Month)).Hit_Count :=
           Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                (Quarter_Item (Month)).Hit_Count + Amount;
        if not Is_Scanner then
            Data.Total.Non_Scanner_Hit_Count := Data.Total.Non_Scanner_Hit_Count + Amount;
            Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                 (Quarter_Item (Month)).Non_Scanner_Hit_Count :=
               Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                    (Quarter_Item (Month)).Non_Scanner_Hit_Count + Amount;
        end if;
        if Is_View then
            Data.Total.View_Count := Data.Total.View_Count + Amount;
            Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                 (Quarter_Item (Month)).View_Count :=
               Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                    (Quarter_Item (Month)).View_Count + Amount;
            if not Is_Scanner then
                Data.Total.Non_Scanner_View_Count := Data.Total.Non_Scanner_View_Count + Amount;
                Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                     (Quarter_Item (Month)).Non_Scanner_View_Count :=
                   Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                        (Quarter_Item (Month)).Non_Scanner_View_Count + Amount;
            end if;
        end if;
    end Record_Amount;


New alternative version [33 lines]:

    procedure Record_Amount (Data : in out Item_Data;
                             Month : in Month_Number;
                             Year : in Report_Year_Number;
                             Amount : in Item_Count := 1;
                             Is_Scanner : in Boolean := False;
                             Is_View : in Boolean := True) is
    begin
        if Data.By_Month (Year, Quarter_Num (Month)) = Empty_Quarter_Item_Data then
            -- We need to allocate an item.
            [Last_Allocated_Quarter_Item_Data] := [] + 1;
            Data.By_Month (Year, Quarter_Num (Month)) :=
               Last_Allocated_Quarter_Item_Data;
        end if;
        [Data.Total.Hit_Count] := [] + Amount;
        [Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
             (Quarter_Item (Month)).Hit_Count] := [] + Amount;
        if not Is_Scanner then
            [Data.Total.Non_Scanner_Hit_Count] := [] + Amount;
            [Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                 (Quarter_Item (Month)).Non_Scanner_Hit_Count] := [] + Amount;
        end if;
        if Is_View then
            [Data.Total.View_Count] := [] + Amount;
            [Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                 (Quarter_Item (Month)).View_Count] := [] + Amount;
            if not Is_Scanner then
                [Data.Total.Non_Scanner_View_Count] := [] + Amount;
                [Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                     (Quarter_Item (Month)).Non_Scanner_View_Count] :=
                   [] + Amount;
            end if;
        end if;
    end Record_Amount;


AI12-0125-3 version [33 lines]:

    procedure Record_Amount (Data : in out Item_Data;
                             Month : in Month_Number;
                             Year : in Report_Year_Number;
                             Amount : in Item_Count := 1;
                             Is_Scanner : in Boolean := False;
                             Is_View : in Boolean := True) is
    begin
        if Data.By_Month (Year, Quarter_Num (Month)) = Empty_Quarter_Item_Data then
            -- We need to allocate an item.
            Last_Allocated_Quarter_Item_Data := @ + 1;
            Data.By_Month (Year, Quarter_Num (Month)) :=
               Last_Allocated_Quarter_Item_Data;
        end if;
        Data.Total.Hit_Count := @ + Amount;
        Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
             (Quarter_Item (Month)).Hit_Count := @ + Amount;
        if not Is_Scanner then
            Data.Total.Non_Scanner_Hit_Count := @ + Amount;
            Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                 (Quarter_Item (Month)).Non_Scanner_Hit_Count := @ + Amount;
        end if;
        if Is_View then
            Data.Total.View_Count := @ + Amount;
            Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                 (Quarter_Item (Month)).View_Count := @ + Amount;
            if not Is_Scanner then
                Data.Total.Non_Scanner_View_Count := @ + Amount;
                Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                     (Quarter_Item (Month)).Non_Scanner_View_Count :=
                     @ + Amount;
            end if;
        end if;
    end Record_Amount;


AI12-0125-2 version: (33 lines)

    procedure Record_Amount (Data : in out Item_Data;
                             Month : in Month_Number;
                             Year : in Report_Year_Number;
                             Amount : in Item_Count := 1;
                             Is_Scanner : in Boolean := False;
                             Is_View : in Boolean := True) is
    begin
        if Data.By_Month (Year, Quarter_Num (Month)) = Empty_Quarter_Item_Data then
            -- We need to allocate an item.
            Last_Allocated_Quarter_Item_Data :+ 1;
            Data.By_Month (Year, Quarter_Num (Month)) :=
               Last_Allocated_Quarter_Item_Data;
        end if;
        Data.Total.Hit_Count :+ Amount;
        Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
             (Quarter_Item (Month)).Hit_Count :+ Amount;
        if not Is_Scanner then
            Data.Total.Non_Scanner_Hit_Count :+ Amount;
            Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                 (Quarter_Item (Month)).Non_Scanner_Hit_Count :+ Amount;
        end if;
        if Is_View then
            Data.Total.View_Count :+ Amount;
            Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                 (Quarter_Item (Month)).View_Count :+ Amount;
            if not Is_Scanner then
                Data.Total.Non_Scanner_View_Count :+ Amount;
                Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                     (Quarter_Item (Month)).Non_Scanner_View_Count :+ Amount;
            end if;
        end if;
    end Record_Amount;

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

From: Bob Duff
Sent: Friday, January 8, 2016  7:52 PM

> (1) Easy to lose the special case in a complex RHS expression. Even
> though @ is a "big" character, it's still only one character. It does
> well in the normal case:
>       Obj.I := @ + 1;
> so we didn't worry about this too much.

Right, I don't buy arguments of the form, "that feature would allow people to
deliberately obfuscate".  If @ buried deep is confusing, then Don't Do That.

> (2) A one character difference can transform an expression into
> something else legal that means something very different.
>
> This originally was raised with the proposal of AI12-0125-2:
>      Obj:+1;     vs. Obj:=+1;
>
> But AI12-0125-3 has the same problem:
>      Obj:=@+1;   vs. Obj:=+1;

I don't get the "one character difference" argument that comes up all the time
(not just in this AI).  I mean, you can write "X := X + 1;"
when you meant "X := X - 1;" and it's just one character different.
It's a real problem, but that sort of thing can happen in every language I
know, and I don't see how to prevent it.

And of course you've made the problem look worse by leaving out the usual
spaces.

>    [X] := [] + 1;  --  A shorthand for X := X + 1;
>
>    [A(I)] := Integer'Max ([], 10); -- A shorthand for A(I) :=
> Integer'Max (A(I), 10);
>
> The square brackets around the LHS keys the reader that (A) this is an
> assignment with a shorthand; (B) the text in brackets is a complete context.
> Thus the use of the target_name shorthand does not harm readability in
> that way.

The idea of marking the LHS somehow as "this will have one or more
shorthands", and then using the shorthands, seems like a great idea.
But I really, REALLY don't like using square brackets for that.
To me, [X] should be a one-element aggregate, and [] should mean
"(null record)" or "(1..0 => junk_value)" or whatever obfuscation
Ada currently requires.

I like your semantics, but unless you can come up with a better syntax,
I say squelch it.  (Sorry, I can't think of better syntax at the
moment.)

> The next message will have an extended example of this idea, and others, in
> a more realistic situation.

I saw the next message, and I think it nicely illustrates that a
shorthand notation can be more readable in real situations.
Thank you for a more realistic example.

I still prefer the @ notation, and I still very much dislike the []
notation.

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

From: Randy Brukardt
Sent: Friday, January 8, 2016  8:05 PM

...
> The idea of marking the LHS somehow as "this will have one or
> more shorthands", and then using the shorthands, seems like a
> great idea.
> But I really, REALLY don't like using square brackets for that.
> To me, [X] should be a one-element aggregate, and [] should
> mean "(null record)" or "(1..0 => junk_value)" or whatever
> obfuscation Ada currently requires.

(others => <>)   ;-)

> I like your semantics, but unless you can come up with a
> better syntax, I say squelch it.  (Sorry, I can't think of
> better syntax at the
> moment.)

Well, one could use {} instead, but I suspect that has the same problem in
your mind (and I didn't want to make Ada even vaguely look like C).

I suppose one could use a keyword for that (and keep @), but no existing
keyword comes to mind, and making a new reserved word seems like overkill:

     let A(I) := @ + 1;

The real example seemed to suggest that there is no real problem with losing
@ so long as one formats the code a bit (if you don't use whitespace, you
can lose just about anything in Ada code!). So maybe this isn't worth
fixing.

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

From: Jeff Cousins
Sent: Saturday, January 9, 2016  6:38 AM

Randy’s suggested change is in my “I could live with” category, but I still
prefer the @ option.

For once Ada will be adding something that looks simple to users, plus it
doesn’t look like C.

PS. Yesterday I found my step - 4 * great grandmother Ann Cousins’ statement
from when she entered the workhouse in 1852.  It uses N÷
rather than @ as a shorthand for “at” when listing previous addresses.

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

From: John Barnes
Sent: Monday, January 11, 2016  7:16 AM

I think I suggesetd the square brackets a long time ago. But it was simply an
example of how to denote the LHS. We could make lhs a reserved word!

I am happy with @.

The one character problem cannot be overccome in a language with the same
symbol being both unary minus and subtraction.

A point of clarity. I presume we can use the @ in any part of an rhs
expression such as a function parameter. Are there any restrictions? Thus

Pig.cat(37) := Func(@);

I should read the AI again.

Oh and Happy New Year.

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

From: Gary Dismukes
Sent: Monday, January 11, 2016  3:36 PM

Right, that's the intention.  There are only minor restrictions.  It's a
onstant view, so it can't be used as an actual for an out parameter in a
function call, and the left-hand side can't be overloaded, but other than
those it can be used generally on the rhs.

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

From: Jean-Pierre Rosen
Sent: Thursday, January 14, 2016  4:33 AM

>    [X] := [] + 1;  --  A shorthand for X := X + 1;
>
>    [A(I)] := Integer'Max ([], 10); -- A shorthand for A(I) :=
> Integer'Max (A(I), 10);
>
> The square brackets around the LHS keys the reader that (A) this is an
> assignment with a shorthand; (B) the text in brackets is a complete context.
> Thus the use of the target_name shorthand does not harm readability in
> that way.

I like the idea of having a more explicit syntax like this one better than
simply @, whose only justification is that it is not already used by the
syntax.

Using "<>" would be tempting, but of course doesn't work because of "(...
with <>)". Other combinations of characters build on the box idea could work:
<->,  <=< (kind of a left arrow)...

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

From: Tucker Taft
Sent: Thursday, January 14, 2016  7:56 AM

One interesting point is that currently "()" is illegal in Ada, as is the use
of parens around the LHS of an assignment.  So we could propose the following:

(This.Is.A.Long.Name[I]) := () + 1;

This avoids introducing a new special character.  I agree with Bob that if we
are going to  start using '[' and ']' there are a lot more interesting things
we should do with it, and it will confuse the heck out of people coming from C
if you can't use [] for indexing.

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

From: Brad Moore
Sent: Thursday, January 14, 2016  8:50 AM

Maybe we are getting hung up on having different characters for the left and
right bookmark. Some good options might involve using the same character for
both.  eg. Maybe the colon? as in;

:X: := :: + 1;

I like this also better if it can be more generally useful. Rather than say
its always a substitution for the lhs of an assignment, this might allow for
substituting an expression that occurs earlier on the rhs.

I have written code like this before where a complex subexpression appears
more than once in a bigger expression.

-- Taken from actual code
Finish := Index_Type'Val
             (Index_Type'Pos (Start) +
               ((Index_Type'Pos (Finish) -
                  Index_Type'Pos (Start)) / 2));

If we allowed such substitution, one could write;

Finish := Index_Type'Val
             (:Index_Type'Pos (Start): +
                ((Index_Type'Pos (Finish) - ::) / 2));

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

From: Randy Brukardt
Sent: Thursday, January 15, 2016  12:33 PM

> Maybe we are getting hung up on having different characters for the
> left and right bookmark.

We're not "hung up" on it. My idea was that the characters used had to look
like bracketing or quoting of some sort. There are 6 sets of such characters
in ASCII: () [] {} <> "" '' and obviously some of these are already used for
something.

> Some good options might
> involve using the same character for both.  eg. Maybe the colon?
> as in;
>
> :X: := :: + 1;

(1) I don't think :: looks like brackets; I think of colon as a separator.
(2) I think using colons for this would make error correction harder.

> I like this also better if it can be more generally useful.
> Rather than say its always a substitution for the lhs of an
> assignment, this might allow for substituting an expression that
> occurs earlier on the rhs.

I thought about that as a possibility, but I rejected it as pretty much
ensuring that the proposal goes nowhere. Loading it up with bells and whistles
is the sure-fire way to kill something optional. Also, that defeats the
primary purpose of making the use of the shorthand more visible. (If you don't
believe that is a need, then @ alone is the best choice, because it's the
shortest possible shorthand and it shows up easily.)

The combination of these ideas would be especially ugly in declarations:

   Obj : Rec (:Func:) := (D => ::, C => Foo);

I could sort of see it using square brackets:

   Obj : Rec ([Func]) := (D => [], C => Foo);

...but this discussion has proved to me that @ really is the best approach,
because it shows up well enough in real code, and finding characters that we'd
agree on for the prefix approach looks unlikely.

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

From: Randy Brukardt
Sent: Thursday, January 14, 2016  12:42 PM

> One interesting point is that currently "()" is illegal in
> Ada, as is the use of parens around the LHS of an assignment.
>  So we could propose the following:
>
> (This.Is.A.Long.Name[I]) := () + 1;
>
> This avoids introducing a new special character.  I agree
> with Bob that if we are going to start using '[' and ']'
> there are a lot more interesting things we should do with it,
> and it will confuse the heck out of people coming from C if
> you can't use [] for indexing.

That seems like an advantage to me. ;-) Seriously, the more different two
programming languages are, the easier it is to program in both. (The most
difficulty I ever had was in the early days of Janus/Ada when the compiler
was still written in Pascal; keeping straight the rules for both languages
was a nightmare.) The fewer syntax similarities with C the better (after
all, that's the reason we're discussing this proposal).

The reason that we ultimately went away from <<>> to using @ was that some
people were concerned about confusion with other uses of the characters,
especially from mistyping. This would be worse, since parens are a common
part of an expression anyway.

I've become convinced, particularly from the "real" examples I created and
posted, that the best solution is indeed @. @ is plenty visible with proper
formatting, and in "causal" reading, you don't need to know that the
short-hand is in use until you reach it. Very few people will ever encounter
the minor semantic differences (how often have you used an overloaded
function of the LHS of an assignment?? I think I've only done it in ACATS
tests).

I'd like to withdraw my [] proposal, but I realize that's rather impossible.
:-)

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

From: Jeff Cousins
Sent: Friday, January 15, 2016  2:12 AM

More seriously, I’m not persuaded that any of the suggested alternatives are
any better than @.

Bracketing the left hand side in particular looks like unnecessary clutter,
and, as someone said, : is a separator.

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

From: Tucker Taft
Sent: Friday, January 15, 2016  8:48 AM

I kind of like "()".  Any comments on that?

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

From: Jeff Cousins
Sent: Friday, January 15, 2016  10:11 AM

Probably the best of the alternatives, but I don’t see any real advantage
over @.

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

From: Bob Duff
Sent: Friday, January 15, 2016  10:54 AM

> I kind of like "()".  Any comments on that?

I don't much like it, although I like it more that "[]".  I don't see any
compelling reason to mark the lhs at all.  Randy gave two reasons, but they
seem pretty weak -- reasonable whitespace solves them.

There's no need for syntax that says, "Pay attention to this lhs, because it's
going to be referred back to soon!"  When you see @, you can go back and look
at the lhs to see what it means.

If I slept through this discussion, from 2010, and woke up in 2022, Rip Van
Winkle-like, and saw () in some Ada code, my best guess as to the semantics
is that it means "empty aggregate".  It doesn't scream "left-hand side".

And finally, I prefer the concise syntax of @.

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

From: John Barnes
Sent: Friday, January 15, 2016  10:56 AM

I would prefer [] to (). Two parens together look awfully like a zero in some
fonts.

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

From: Tucker Taft
Sent: Friday, January 15, 2016  11:00 AM

> Probably the best of the alternatives, but I don’t see any real advantage
> over @.

The main advantage of "()" is that it avoids adding another special character
to Ada's lexical character set.  "@" smacks a bit of a C-like construct, where
there are a zillion special characters like !, ~, ^, &, %, ... making the code
look a bit like chicken scratchings.  A construct like "()" seems more nearly
English, and generally feels more consistent with the rest of Ada syntax.

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

From: Brad Moore
Sent: Friday, January 15, 2016  11:53 AM

If I woke up from the rip van winkle effect, or just as someone completely
new to the language, I think I would notice both the parens on the LHS, and
the empty parens, and would probably be able figure out that the empty parens
are referring to the text in the LHS parents.

If I saw the @ however, the syntax would not give any such clue. In fact I
would likely to think this is some sort of non-standard preprocessor macro
substitution is being applied. It reminds me of assembler macros I've seen
in the distant past. The parens however are obviously syntax and part of the
language.

Another couple of tweaks to Tuckers suggestion...

In for loops we have  syntax  to allow
     for I in 1 .. 10 loop

the ".." can be viewed as a substitution for all the numbers between 1 and 10
in this example.

Suppose instead of () from Tuckers suggestion, we used (..)

The .. here similarly would imply all the text between '(' and ')' from the LHS

This would also address John's complaint about () being mistaken for a 0.

This would allow;

(Really_Really_Long_Name(I)) := (..) + 1;

A second optional tweak would be to the RHS, instead of "(" and ")", one would
use "(." and ".)"

ie.

(. Really_Really_Long_Name(I) .) := (..) + 1;

This might provide a further visual clue about what the (..) is referring to,
plus it has the advantage that if we ever decided down the road to allow
similar substitutions on the RHS, we could add that to the language in a
compatible manner.

eg.   Answer := (. Really_Really_Long_Name(I) .) * 2 + (..) mod 2;

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

From: Tucker Taft
Sent: Friday, January 15, 2016  1:07 PM

I like your "(..)" idea.  Definitely the best looking and most Ada-like
suggestion so far.

I don't think we should try to generalize this to represent anything but the
left-hand side, because you can easily get into order-of-evaluation problems,
and more complex overload resolution situations.

I am not sure about whether we need to parenthesize the LHS, but I at least
could live with either solution.  Having to parenthesize the LHS does make the
whole thing somewhat heavier.

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

From: Randy Brukardt
Sent: Friday, January 15, 2016  1:06 PM

> I kind of like "()".  Any comments on that?

It's not one of your better ideas. ;-)

(1) As John noted, from 20000 feet it looks like a zero.   A := () + B;  vs
    A := 0 + B;  That's insufficiently different;
(2) Parens are normally present in expressions anyway, so there is a danger
    in accidentally converting something into this shorthand;
(3) Parens in an expression are usually a meaningless grouping agent, so one
    is trained to ignore them and focus on the ids and operators;
(4) The meaning isn't intuitive - Bob says he thinks its an empty aggregate;
(5) I tend to use () as a short-term TBD when constructing larger expressions,
    for instance when I have to go look up the name of something.
If I'm interrupted and forget to finish the expression, the compiler will tell
me that it is incomplete. (If I expect it to be a while before I come back to
it, then of course I'll use a comment as well, but for the short term, writing
a comment and then deleting it is wasted programming time.)

> The main advantage of "()" is that it avoids adding another special
> character to Ada's lexical character set.

We discussed that in Bennington in the context of whether to use "<<>>" (a
better choice, IMHO, if we're sticking with existing characters only) or to
use a new character. The discussion and vote was pretty clear that an
additional character is better than trying to overload a meaning which is
guaranteed to cause confusion.

Adding one special character is hardly going to turn the syntax into C (and in
any case, Ada needs more operators; it's not the special characters that bother
me about C, it's the overly concise syntax for statements and declarations).

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

From: Dan Eilers
Sent: Friday, January 15, 2016  1:17 PM

> This would allow;
>
> (Really_Really_Long_Name(I)) := (..) + 1;

Or how about:

   Really_Really_Long_Name(I) := ... + 1;

or

   temp renames Really_Really_Long_Name(I) := temp + 1;

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


From: Stephen Michell
Sent: Friday, January 15, 2016  1:14 PM

I think that Brad's proposal is the best so far, but no mandatory brackets
on the left.

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

From: Jeff Cousins
Sent: Friday, January 15, 2016  1:30 PM

I think it would be clear from the context that @ was an operand not an
operator, and it can’t be too hard to learn what a single character means.

Though I quite like the argument for (..) that .. implies substitution,
remember that we would probably have gone for <> due to is implication of
substitution if it wasn’t already taken for other purposes.

But I still don’t like the idea of bracketing the LHS, it’s just clutter,
double brackets such as the )) in

(Really_Really_Long_Name(I)) := (..) + 1;
just cause confusion.

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

From: John Barnes
Sent: Friday, January 15, 2016  2:49 PM

How about three dots rather than two? Thus

Sow.piglets := (...) + 1;

I rather like that.

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

From: Brad Moore
Sent: Friday, January 15, 2016  2:57 PM

> Though I quite like the argument for (..) that .. implies
> substitution, remember that we would probably have gone for <> due to
> is implication of substitution if it wasn’t already taken for other
> purposes.
>
> But I still don’t like the idea of bracketing the LHS, it’s just
> clutter, double brackets such as the )) in
>
> (Really_Really_Long_Name(I)) := (..) + 1; just cause confusion.

We could say that the LHS parens are redundant and thus are optional and can
be eliminated.

Or we could go further and say that since the LHS parens are always redundant,
they are not allowed and illegal. We could use this as an explanation to
justify the (..) syntax on the LHS.

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

From: Randy Brukardt
Sent: Friday, January 15, 2016  3:08 PM

> How about three dots rather than two? Thus
>
> Sow.piglets := (...) + 1;
>
> I rather like that.

That would be the first lexeme with three characters (or five, depending on
the rules), which might cause some problems for tools.

In general, I think the implementation cost is about the same for a new
character vs. a new symbol based on existing characters, and possibly the
latter would be more expensive because it would have the potential to break
some of the invariants surrounding lexing Ada code.

Brad's (..) could be the cheapest of all, as it could be actually three
existing symbols syntactically put together ("(", "..", and ")"). No lexical
changes would definitely be an advantage for a proposal.

Readability-wise, I don't mind this anywhere near as much as I dislike
Tucker's "()".

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

From: Randy Brukardt
Sent: Friday, January 15, 2016  3:10 PM

> Or we could go further and say that since the LHS parens are always
> redundant, they are not allowed and illegal.

The LHS is a name, parens are not allowed now. So we don't have to say
anything to not allow something that is already not allowed.

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

From: Bob Duff
Sent: Friday, January 15, 2016  4:02 PM

> If I woke up from the rip van winkle effect, or just as someone
> completely new to the language, I think I would notice both the parens
> on the LHS, and the empty parens, and would probably be able figure
> out that the empty parens are referring to the text in the LHS
> parents.

Really?  If I woke up from the winkle effect, and saw:

(Really_Really_Long_Name(I)) := () + 1;

I would think, "Great.  ARG has finally got rid of the silly syntactic
distinction between 'name' and 'expression'.  Everything is 'expression', and
you can always parenthesize an expression -- that's used for grouping, and has
no other effect.  I would think "(Really_Really_Long_Name(I))" means the exact
same thing as "Really_Really_Long_Name(I)", as indeed it already does on the
right-hand side.

In C, you can say:

    (foo.bar[2]) += 2;

and it means the exact same thing as:

    foo.bar[2] += 2;

C is better than Ada in this regard.

X+Y is just a shorthand for "+"(X, Y), right?  Well, no, you can say:

    "+"(X, Y)'Image

but you can't say:

    (X + Y)'Image

but you CAN say:

    T'(X + Y)'Image

> If I saw the @ however, the syntax would not give any such clue.

It's fine to not give a clue.  Most of the suggested syntaxes (..), (...),
..., ::, @ don't give a clue.  You have to learn the language.
But parens already have meaning, so they give a WRONG clue, which is worse.

I don't see why folks are pushing for more and more verbose syntaxes (none of
which give clues).  C programmers who write:

    some[long].name++;

will laugh at us, and rightly so.

I wrote:

> And finally, I prefer the concise syntax of @.

Still.  ;-)

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

From: John Barnes
Sent: Friday, January 15, 2016  4:24 PM

I suggested three dots because it is the ellipsis linguistic symbol meaning
"an omission of one or more words..."

The trouble with two dots is that it is embedded in every Ada person's brain
as a range.

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

From: Randy Brukardt
Sent: Friday, January 15, 2016  4:28 PM

> > And finally, I prefer the concise syntax of @.
>
> Still.  ;-)

What he said. :-)

Keep in mind this is supposed to be a "shorthand"! It doesn't do for it to get
very verbose. Brad and Dan both suggested syntaxes that took 8 or more
characters for the "shorthand". That could get nearly as tedious as the
original Ada form. Recall (part of) the example I posted last week:

        Data.Total.Hit_Count := Data.Total.Hit_Count + Amount;
        Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
             (Quarter_Item (Month)).Hit_Count :=
           Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
                (Quarter_Item (Month)).Hit_Count + Amount;

Using '@':

        Data.Total.Hit_Count := @ + Amount;
        Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
             (Quarter_Item (Month)).Hit_Count := @ + Amount;

Using "(..)":

        (.Data.Total.Hit_Count.) := (..) + Amount;
        (.Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
             (Quarter_Item (Month)).Hit_Count.) := (..) + Amount;

Using Dan's inline renames:

        LHS renames Data.Total.Hit_Count := LHS + Amount;
        LHS renames Quarter_Item_Data (Data.By_Month (Year, Quarter_Num (Month)))
             (Quarter_Item (Month)).Hit_Count := LHS + Amount;

It's pretty clear to me which of these is the most readable. But I'll let you
draw your own conclusions (it's @ :-).

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

From: Bob Duff
Sent: Friday, January 15, 2016  4:32 PM

> The main advantage of "()" is that it avoids adding another special
> character to Ada's lexical character set.

I see that as a DISadvantage -- creating new notations is better than creating
new meanings for existing notations.  Yeah, I know "(name)" is not allowed on
the lhs of an assignment, but if it were it would/should mean the same thing
as "name".

By the way, nobody has explained very well why one should be required to mark
the left-hand side with any syntax at all, just because you want to use this
new feature.

>..."@" smacks a bit of a C-like construct, where there are a zillion
>special characters like !, ~, ^, &, %, ... making the code look a bit
>like chicken  scratchings.

Yeah, I guess.  I prefer "not" over "!" and "and" over "&&" and
"Shift_Left(...)" over "... << ...".

For the same reason, I'd prefer a keyword instead of @.  But I thought we
discussed that at the last meeting, and we were worried that WG9 can't stomach
non-reserved keywords, and I (we?) can't stomach an incompatibility here.  So
we're stuck with some sort of weird symbol(s), and we should choose something
not easily mistaken for something else, and something concise.

>...A construct like "()" seems more nearly English, and generally feels
>more consistent with the rest of Ada syntax.

To me, it feels more overloaded/confused with existing Ada syntax.
As to English, well analogies with natural language are weak, IMHO, but
anyway, parens are used for grouping (i.e. parenthetical clauses) in English,
but you want to use them as a pronoun in Ada.

As I said, I could live with (), or with some of the other suggestions.
I think the main goal is:

    You can refer to the lhs without repeating it verbatim,
    and without creating a name for it.

The secondary goal is:

    Be concise.

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

From: John Barnes
Sent: Friday, January 15, 2016  4:35 PM

I must admit that the jolly old @ does stand out.

J@hn

That's odd, Mr Gates has underlined it.

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

From: Bob Duff
Sent: Friday, January 15, 2016  5:35 PM

Looks like an email address.  I'm impressed that you own a two-letter domain
name, "hn".  ;-)

By the way, I think I would be annoyed if the ellipsis "..." would become an
Ada token, because then how would I write examples, like:

    if ... then
        ... -- do some work
    end if;

And by the by the way, when teaching Ada, I once had a question, "What does <>
mean in Ada?".  Well, it means all sorts of things.  My point is, let's not
make the question "What do parens mean in Ada?" harder.

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

From: Stephen Michell
Sent: Friday, January 15, 2016  5:11 PM

So, thinking about the keyword issue, I am thinking that we are often nervous
about a new keyword conflicting with names in user code. Why not consider
using "ditto" as the keyword. Hence Some_Really_Long_Name := ditto + 1;  Every
English speaker recognizes what "ditto" means, so brain interpretation is
instantaneous.

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

From: Randy Brukardt
Sent: Friday, January 15, 2016  6:13 PM

...
> >..."@" smacks a bit of a C-like construct, where there are a zillion
> >special characters like !, ~, ^, &, %, ... making the code look a bit
> >like chicken  scratchings.
>
> Yeah, I guess.  I prefer "not" over "!" and "and" over "&&"
> and "Shift_Left(...)" over "... << ...".
>
> For the same reason, I'd prefer a keyword instead of @.  But I thought
> we discussed that at the last meeting, and we were worried that WG9
> can't stomach non-reserved keywords, and I
> (we?) can't stomach an incompatibility here.  So we're stuck with some
> sort of weird symbol(s), and we should choose something not easily
> mistaken for something else, and something concise.

I don't view it as "stuck". I think a keyword would be worse than a character
or symbol for this use. A keyword would more easily get lost in an expression,
because it would just look like an identifier to a reader that didn't know
about the new feature. (Or even one that did know about the feature!)

If I got some Ada code reading,

        Data.Total.Hit_Count := @ + Amount;

I'd know there is something to figure out, and I'd go look in an Ada manual or
ask a question on comp.lang.ada or stackoverflow.

If I got instead:

       Data.Total.Hit_Count := ditto + Amount;

(using Steve's suggestion), I'd probably waste a lot of time trying to figure
out where "ditto" is defined.

I suppose if you are using an editor that colors or boldfaces keywords, it
would be less of an issue, but not everyone uses those (by choice or
requirements). Note that we don't have that in our code examples here.

Additionally, I don't see what "non-reserved keywords" has to do with this
case. This isn't like "some", that only is used in a single context where no
identifier can be used. These appear in arbitrary expressions. And we surely
couldn't allow adding a declaration for "ditto" to change the meaning of
assignments that just happen to be in scope. (And we couldn't just ignore such
a declaration, either, that would be pure evil.) So, I don't see how we could
avoid having a keyword used in this context be fully reserved.

Anyway, I think I prefer this to be lexically distinct from anything that might
appear in an Ada 2012 expression. And conciseness is important, thus @ is still
leading in my mind.

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

From: Stephen Michell
Sent: Friday, January 15, 2016  7:05 PM

I was proposing ditto as a reserved keyword.

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

From: Tucker Taft
Sent: Friday, January 15, 2016  7:53 PM

No need to keep complaining about "()". I prefer "(..)" which I believe is
visible, short, and nicely Ada-ish.

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

From: Brad Moore
Sent: Friday, January 15, 2016  9:19 PM

...
> By the way, nobody has explained very well why one should be required
> to mark the left-hand side with any syntax at all, just because you
> want to use this new feature.

I am totally OK with dropping the LHS parens, and I sense that others are as
well. I agree that they are unnecessary and just add clutter.

>> ..."@" smacks a bit of a C-like construct, where there are a zillion
>> special characters like !, ~, ^, &, %, ... making the code look a bit like chicken
>> scratchings.
>
> Yeah, I guess.  I prefer "not" over "!" and "and" over "&&" and
> "Shift_Left(...)" over "... << ...".
>
> For the same reason, I'd prefer a keyword instead of @.  But I thought
> we discussed that at the last meeting, and we were worried that WG9
> can't stomach non-reserved keywords, and I (we?) can't stomach
> an incompatibility here.  So we're stuck with some sort of weird
> symbol(s), and we should choose something not easily mistaken
> for something else, and something concise.
>

Here's a relevant quote from para 7 of page 1 of the RM. (Design Goals)

"The need for languages that promote reliability and simplify
maintenance is well established. Hence emphasis was placed on program
readability over ease of writing."

  ...   -- No pun intended...  (OK maybe that one)

"Furthermore, error-prone notations have been avoided, and the syntax of
the language avoids the use of encoded forms in favor of more
English-like constructs. "

This suggests that it should be unimportant whether one has to type 4
characters instead of 1, and @ is definitely an encoded form, so it
should be avoided.

The @ character has no association with text replacement, so it is
entirely a cryptic notation, which seems to not be in the spirit of the
Ada design goals.

I'd prefer to see something that conveys meaning, ideally an English
word, such as ditto, like Steve suggests, but if new keywords are
problematic, the next best thing would be some symbolic representation
that has similar meaning.

We've been discussing ..., since it is a symbolic representation of
ellipses which indicate an omission, but ditto seems closer to what we
are trying to convey since it means that the text above(~before) is
repeated.

The symbolic representation of ditto of course is ", but I recognize
there would be confusion and parsing problems if we used the double
quote character alone.

One could however look at the convention used Polish, Czech, and
Austrian German, which is to use the ditto mark surrounded by em-dashes.

ie.  -"-

According to;
    https://en.wikipedia.org/wiki/Iteration_mark

So this would give us...

Really_Really_Long_Name(I) := -"- + 1;

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

From: Ben Brosgol
Sent: Friday, January 15, 2016  9:25 PM

I haven't been following this complete thread, so apologies if this has
been already suggested and trashed, but there's also "(.)" which maybe is
more unix-ish than Ada-ish. But the dot carries the idea of "here", and the
construct would be one character shorter than "(..)" which has the look and
feel of a range as others have noted.

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

From: John Barnes
Sent: Saturday, January 16, 2016  5:58 AM

This could go on for ever.

For me @ is much more visible than something like (..) but if you like
brackets around something then why not (@)?

Highly visible and we get a syntax error if a bit is left out.

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

From: Erhard Ploedereder
Sent: Saturday, January 16, 2016  11:56 AM

What I believe you need to specify somewhere is that side-effects have no
influence on whatever the repetition is supposed to designate.

That is (for the () proposal), in
(A(I)) := change_the_I_or_the_access_A(42) * (); the semantics are
well-defined. Or do you want textual macros?(Uggh. I hope not.)

This may have already been said in an early message but all the discussion
over syntax should not loose the thought.

-----------------
A few more concerns about the reuse of existing operators and about the
marking of the LHS. If (lhs) can only be applied to the entire LHS, is is
useless clutter, plus this already noted "oops an illegal paranthesized
expression LHS" - complication to semantic rules interacting with syntax.

If it can be applied more broadly, what does
A(I) := 42 * ();
mean? Presumably illegal, since the () are the indexing op?

What about
A(4*(I)) := 42 * ();
A((I)) := ();
Hmm. Is ()=I?
----------

Of all the existing proposals, I like @ or ?.
I hate "()" - too small a hamming distance to other programs.
(...) is read as "to be filled in later"
(.) is better but looks like Sumerian writing :-) .

And I am dead set against marking the LHS, if only full LHS can be marked.

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

From: Erhard Ploedereder
Sent: Saturday, January 16, 2016  11:46 AM

I am going to add another possibility to the fray, this one derived from
pattern matching language:
 ThisIsVery_Long_andUgly(i)! := ? + 1;

The meaning of the operators ! and ? are obvious, I trust.
! is a postfix view operator on names, i.e. it does nothing to the name, but
it remembers the location/entity denoted by the name.

Pro: No way to misread this syntax. Syntax known to some in pattern languages.
No question on what do do, when operator is applied to non-name expressions
(syntactically/semantically excluded). Can be applied to any part of the LHS,
as in:

A(complicated.index.name.getit!) := B(?);

Con: two more special symbols.

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

From: Jean-Pierre Rosen
Sent: Sunday, January 17, 2016  1:30 AM

At this point, it's clear that there are several acceptable possibilities,
everybody on this list has it's pet one (my preference would be for (..)),
and it will be hard to get consensus.

We've been accused every now then of deciding everything from our ivory tower.
Since the chosen syntax is mainly a matter of taste, why not ask the community
and make a public poll? We can choose a small number of solutions, make a
doodle, and advertise it on c.l.a, AE and SigAda mailing lists, and other
similar groups.

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

From: Jeff Cousins
Sent: Sunday, January 17, 2016  3:05 AM

That sounds like a recipe for indecision to me.
Can we complete our vote on @ and see what its conclusion is?

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

From: Bob Duff
Sent: Sunday, January 17, 2016  2:53 PM

> I am totally OK with dropping the LHS parens, and I sense that others
> are as well. I agree that they are unnecessary and just add clutter.

OK, sounds good.

> Here's a relevant quote from para 7 of page 1 of the RM. (Design
> Goals)
>
> "The need for languages that promote reliability and simplify
> maintenance is well established. Hence emphasis was placed on program
> readability over ease of writing."
>
>   ...   -- No pun intended...  (OK maybe that one)

It took me a while to understand what pun you were referring to!  ;-)

> "Furthermore, error-prone notations have been avoided, and the syntax
> of the language avoids the use of encoded forms in favor of more
> English-like constructs. "

Well, I'm sure we all agree with the above platitudes.  But the only way to
be English-like would be a keyword.  You can't use the above to argue for @
over (..) or vice versa -- ALL of the non-keyword suggestions are not
English, and have no intuitive meaning, so people will need to memorize what
it means.

I suppose I half-agree with your argument that "..." is somewhat intuitive.
But I would hate to make that part of the syntax, because I am in the habit
of explaining things with examples that use that to leave out irrelevant
parts, as in:

    function F(...) return String;
    ...
    X: String := F(...);

I feel pretty strongly about that; "..." is hugely useful in discussions
about programming languages.

> This suggests that it should be unimportant whether one has to type 4
> characters instead of 1, and @ is definitely an encoded form, so it
> should be avoided.

It's not typing; the conciseness is for readability.

> So this would give us...
>
> Really_Really_Long_Name(I) := -"- + 1;

So the compiler has to look ahead to distinguish the above from:

    Really_Really_Long_Name(I) := -"- + 1";

Does the following use a maximal munch rule?

    Really_Really_Long_Name(I) := -"- + 1" + -"-;

Hmm.  I suggest we avoid using quote marks in any way, here.

Anyway, there's nothing intuitive about that for me.
I'd have to look it up and memorize what it means, just like most of the other
suggestions.

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

From: Bob Duff
Sent: Sunday, January 17, 2016  2:54 PM

> What I believe you need to specify somewhere is that side-effects have
> no influence on whatever the repetition is supposed to designate.
>
> That is (for the () proposal), in
> (A(I)) := change_the_I_or_the_access_A(42) * (); the semantics are
> well-defined. Or do you want textual macros?(Uggh. I hope not.)

Right, I'm pretty sure everybody agrees we don't want macro-expansion
semantics here!  In fact, one of the benefits of this feature is:

    A(F(X)) := A(F(X)) + 1;

We don't know if F(X) returns the same value both times, but:

    A(F(X)) := @ + 1;

We know it's not an issue.

At the meeting, people suggested wording via various equivalences.
I suggested using the kind of wording that you'd use to explain it to a normal
person:

    @ denotes the value of the left-hand side, but the left-hand side is
    evaluated only once.

I think everybody else wanted to obfuscate.  ;-) ;-)

> This may have already been said in an early message but all the
> discussion over syntax should not loose the thought.
>
> -----------------
> A few more concerns about the reuse of existing operators and about
> the marking of the LHS.
> If (lhs) can only be applied to the entire LHS, is is useless clutter,
> plus this already noted "oops an illegal paranthesized expression LHS"
> - complication to semantic rules interacting with syntax.
>
> If it can be applied more broadly, what does
> A(I) := 42 * ();
> mean? Presumably illegal, since the () are the indexing op?
>
> What about
> A(4*(I)) := 42 * ();
> A((I)) := ();
> Hmm. Is ()=I?

Right, all good reasons to avoid overgeneralizing this.

> ----------
>
> Of all the existing proposals, I like @ or ?.
> I hate "()" - too small a hamming distance to other programs.
> (...) is read as "to be filled in later"
> (.) is better but looks like Sumerian writing :-) .
>
> And I am dead set against marking the LHS, if only full LHS can be
> marked.

I'm against marking the LHS, dot.

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

From: Tucker Taft
Sent: Sunday, January 17, 2016  6:10 PM

> What I believe you need to specify somewhere is that side-effects have
> no influence on whatever the repetition is supposed to designate.

The semantics are of a rename of the LHS.

> That is (for the () proposal), in
> (A(I)) := change_the_I_or_the_access_A(42) * (); the semantics are
> well-defined. Or do you want textual macros?(Uggh. I hope not.)
>
> This may have already been said in an early message but all the
> discussion over syntax should not loose the thought.

I believe it was captured in the original AIs.

...
> Of all the existing proposals, I like @ or ?.
> I hate "()" - too small a hamming distance to other programs.
> (...) is read as "to be filled in later"
> (.) is better but looks like Sumerian writing :-) .

And what about "(..)" suggested by Brad, and preferred by myself, and I
believe JP?

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

From: Bob Duff
Sent: Sunday, January 17, 2016  2:50 PM

> If I got some Ada code reading,
>
>         Data.Total.Hit_Count := @ + Amount;
>
> I'd know there is something to figure out, and I'd go look in an Ada
> manual or ask a question on comp.lang.ada or stackoverflow.
>
> If I got instead:
>
>        Data.Total.Hit_Count := ditto + Amount;
>
> (using Steve's suggestion), I'd probably waste a lot of time trying to
> figure out where "ditto" is defined.
>
> I suppose if you are using an editor that colors or boldfaces
> keywords, it would be less of an issue, but not everyone uses those
> (by choice or requirements). Note that we don't have that in our code
> examples here.

I don't like those color-izing editors.  But Rip should know that keywords are
typically in lower case.  ;-)

> Additionally, I don't see what "non-reserved keywords" has to do with
> this case. This isn't like "some", that only is used in a single
> context where no identifier can be used. These appear in arbitrary
> expressions. And we surely couldn't allow adding a declaration for
> "ditto" to change the meaning of assignments that just happen to be in
> scope. (And we couldn't just ignore such a declaration, either, that
> would be pure evil.) So, I don't see how we could avoid having a keyword
> used in this context be fully reserved.

Easy:  If the compiler sees a declaration of "ditto", it gives a diagnostic
message.  (Yet another example where requiring messages is beneficial.) Within
the scope of that decl, "ditto" refers to the declaration; it's not the
keyword.  I guess it should warn on every such USE of ditto as well.

In other words, it's a preference rule.  And preference rules are evil, so we
want diagnostic messages.

BTW, I don't much like "ditto".  I'd call it "it".  Or "lhs" is tolerable.

Anyway, if nobody is seriously proposing a keyword (reserved or not), we don't
need to settle these issues.

> Anyway, I think I prefer this to be lexically distinct from anything
> that might appear in an Ada 2012 expression. And conciseness is
> important, thus @ is still leading in my mind.

OK with me.

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

From: Stephen Michell
Sent: Sunday, January 17, 2016  8:05 PM

The reason that I suggested “ditto” is that I believe that it could be a
keyword (reserved) and not interfere with projects using “Ditto”. Ditto is
such a specialized word in English that I suspect that it would be used in
names of functions, types, or objects names. The fact that it is lowercase and
bolded or coloured in many editors would give a strong clue that it is
reserved.

I am against using specialized symbols such as @ or (). Ada almost never uses
symbols that are not words or mathematical symbols. It is very much out of
character to start introducing such symbols now.

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

From: John Barnes
Sent: Monday, January 18, 2016  2:04 AM

The key thing in favour of @ for me is that it stands out like a sore thumb.
I hate () because it looks so much like zero or o.

Let's vote.

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

From: Brad Moore
Sent: Monday, January 18, 2016  6:20 AM

I have a question about the (..) idea.

If we have given up the idea of bracketing the lhs, is there still value in
having the parens around ".."?

Otherwise, I think it is an improvement to just use .., as in.

Really_Long_Name := .. + 1;

This would address Bob's concern about overloading parens in the syntax, and
also has the advantage of not introducing new symbols to the language.  The
existing use of .. is a shorthand for inclusion, and seems to be compatible
with this new shorthand, as a form of inclusion of text from the lhs.

I agree with others that using a keyword for this purpose is problematic
because it is more ambiguous and therefore confusing. The problem is that the
keyword appears in the same place that a name can appear, so I can see that
people would get confused and look for a variable or constant declared
somewhere with the same name. Add to that the backwards compatibility problems
of adding new keywords, and that pretty much kills that idea, in my mind.

I like .. better than @ because its an existing symbol, which all existing Ada
programmers would recognize, and because this new usage has some compatibility
with the existing usage, in the sense that it is a form of inclusion.

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

From: Randy Brukardt
Sent: Monday, January 18, 2016  3:56 PM

> What I believe you need to specify somewhere is that side-effects have
> no influence on whatever the repetition is supposed to designate.

We discussed that extensively in Bennington; all of the proposals nail down
the semantics carefully. (See the current draft of AI12-0125-3 specifically).

The only real open issue (other than editorial wording changes) is the actual
syntax in question. Which *everyone* has an opinion on.

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

From: Randy Brukardt
Sent: Monday, January 18, 2016  4:05 PM

...
> At the meeting, people suggested wording via various equivalences.
> I suggested using the kind of wording that you'd use to explain it to
> a normal person:
>
>     @ denotes the value of the left-hand side, but the left-hand side is
>     evaluated only once.
>
> I think everybody else wanted to obfuscate.  ;-) ;-)

Everyone else wants preciseness. We want it obvious how to answer questions
about erroneousness, finalization, accessibility, and the like.

We tried it your way with 'Old, and we ended up spending twice as long to
redo it so the semantics are nailed down. The same would hold here.

(Besides, this would be inconsistent with the rest of the standard. We never
say things like "evaluated only once". And what is getting evaluated? The LHS
isn't a value at all. Etc.)

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

From: Randy Brukardt
Sent: Monday, January 18, 2016  4:13 PM

...
> > Additionally, I don't see what "non-reserved keywords" has to do
> > with this case. This isn't like "some", that only is used in a
> > single context where no identifier can be used. These appear in
> > arbitrary expressions. And we surely couldn't allow adding a
> > declaration for "ditto" to change the meaning of assignments that
> > just happen to be in scope. (And we couldn't just ignore such a
> > declaration, either, that would be pure evil.) So, I don't see how
> > we could avoid having a keyword used in this context be fully reserved.
>
> Easy:  If the compiler sees a declaration of "ditto", it gives a
> diagnostic message.  (Yet another example where requiring messages is
> beneficial.)  Within the scope of that decl, "ditto" refers to the
> declaration; it's not the keyword.  I guess it should warn on every
> such USE of ditto as well.
>
> In other words, it's a preference rule.  And preference rules are
> evil, so we want diagnostic messages.

So let me get this straight: in order to avoid making it reserved, you want
to require every use of the keyword as an identifier to have a soft error?
Which you'd have to suppress to continue? How is that any sort of advantage
over having it reserved? Plus you'd still have the danger of someone making
a compiler which turns these warnings off (Robert used to threaten that about
GNAT and soft errors) and then the meaning changing silently.

I mean, this is the sort of thinking that gives non-reserved keywords a bad
name. In the cases of "overriding" and "some", the keyword can only appear
syntactically where no identifier is allowed, so there can be no confusion
and no need for any sort of warning. So those make sense (even though we've
never been able to convince enough people of that value). If the keyword can
appear somewhere a name can, the only possibility is confusion, no matter what
rules are adopted.

> BTW, I don't much like "ditto".  I'd call it "it".  Or "lhs"
> is tolerable.
>
> Anyway, if nobody is seriously proposing a keyword (reserved or not),
> we don't need to settle these issues.

Steve Michell has seriously proposed "ditto". Twice. I guess since he's not an
ARG member, he doesn't count?

I'm strongly against such a proposal, as the confusion possibilities are too
much.

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

From: Randy Brukardt
Sent: Monday, January 18, 2016  4:19 PM

> That sounds like a recipe for indecision to me.

I agree. I don't think the exact syntax matters all that much; whatever we
choose, everyone will get used to.

So long as we don't chose anything severely bad: whatever we chose should not
easily be confused with some existing Ada use (thus a keyword is out [confused
with a name of a declaration], () is out [confused with zero], and probably ..
is out [confused with a range]).

The other issues of conciseness and naturalness are secondary. And I agree
with Bob that the latter is probably not possible (or important).

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

From: Randy Brukardt
Sent: Monday, January 18, 2016  4:25 PM

> I have a question about the (..) idea.
>
> If we have given up the idea of bracketing the lhs, is there still
> value in having the parens around ".."?
>
> Otherwise, I think it is an improvement to just use .., as in.
>
> Really_Long_Name := .. + 1;

I don't think this works. Ada users expect ".." to mean a range. And ranges
can appear in expressions:

  Long_Object_Name := (if Long_Object_Name in 1 .. 10 then 0 else Long_Object_Name - 10);

Using '@' improves the readability of this expression:

  Long_Object_Name := (if @ in 1 .. 10 then 0 else @ - 10);

Use "..", umm, does not:

  Long_Object_Name := (if .. in 1 .. 10 then 0 else .. - 10);

Using "(..)" (or "(.)") would be OK, I think:

  Long_Object_Name := (if (..) in 1 .. 10 then 0 else (..) - 10);

But @ still seems to lead.

Moral: Examples help! (Even if they are a bit contrived.)

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

From: Bob Duff
Sent: Monday, January 18, 2016  4:25 PM

>... And what is getting evaluated? The
> LHS isn't a value at all.

The LHS is a name, and names are evaluated.
See 5.2(7).

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

From: Randy Brukardt
Sent: Monday, January 18, 2016  4:38 PM

> >... And what is getting evaluated? The  LHS isn't a value at all.
>
> The LHS is a name, and names are evaluated.
> See 5.2(7).

Sure, but your wording talked about "the value of the LHS" or something like
that. Nothing about the name of the LHS at all.

Preciseness of wording is a virtue. :-)

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

From: Bob Duff
Sent: Monday, January 18, 2016  4:58 PM

> Sure, but your wording talked about "the value of the LHS" or
> something like that.

No, it didn't.  It talked about "the left-hand side".  But I guess you're
right -- the syntactic term is "the variable_name".  The term "left-hand side"
is used only in the AARM annotations.  But it's quite clear that "the
left-hand side" in the AARM is synonymous with "the variable_name".

>... Nothing about the name of the LHS at all.

The left-hand side IS a name. The "name OF the left-hand side" makes no sense.

> Preciseness of wording is a virtue. :-)

Indeed.

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

From: Bob Duff
Sent: Monday, January 18, 2016  5:45 PM

> So let me get this straight: in order to avoid making it reserved, you
> want to require every use of the keyword as an identifier to have a soft error?

Yes.  Or maybe just every declaration.

> Which you'd have to suppress to continue?

No.  The whole point of soft errors is to give people a choice:
If it's expensive to modify code in your project, you can choose to ignore the
errors, and your program will still mean what it used to mean.  That's
"compatibility".  On the other hand, if you want to maximize readability, you
can change all the uses of "Ditto" as an identifier to something else, and
then start using "ditto" as a keyword.

You can phase in such changes over many months, if you like.
These decisions should be up to Ada project managers, not ARG.

>...How is that any sort of advantage
> over having it reserved?

Ask Jeff.  Same reason as with "interface".

>...Plus you'd still have the danger of someone making  a compiler which
>turns these warnings off (Robert used to threaten that  about GNAT and
>soft errors) and then the meaning changing silently.

I've no idea what you mean.  If the RM requires a diagnostic message, then
we can have an ACATS test that tests conformance with that rule.
And of course one can write a nonconforming implementation of not-quite-Ada,
and of course compilers can have switches that turn messages on and off.  So
what?

> I mean, this is the sort of thinking that gives non-reserved keywords
> a bad name. In the cases of "overriding" and "some", the keyword can
> only appear syntactically where no identifier is allowed, so there can
> be no confusion and no need for any sort of warning.

On the contrary, if nonreserved keywords existed, I think they deserve a
warning.  And certainly the people who think nonreserved keywords are evil
think that it's confusing to use a keyword as an identifier, no matter where
in the syntax.  I would think soft errors would make those people more
comfortable with the idea.

>...So those make sense (even though we've  never been able to convince
>enough people of that value). If the keyword can  appear  somewhere a
>name can, the only possibility is confusion, no matter  what rules are
>adopted.
>
> > BTW, I don't much like "ditto".  I'd call it "it".  Or "lhs"
> > is tolerable.
> >
> > Anyway, if nobody is seriously proposing a keyword (reserved or
> > not), we don't need to settle these issues.
>
> Steve Michell has seriously proposed "ditto". Twice. I guess since
> he's not an ARG member, he doesn't count?

I can't keep track of who proposed what, nor how serious they are.
I stand by my statement "IF (IF!) nobody is seriously proposing a keyword
(reserved or not), we don't need to settle these issues."
My statement doesn't say anything about who "counts".

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

From: Randy Brukardt
Sent: Monday, January 18, 2016  6:23 PM

> > Which you'd have to suppress to continue?
>
> No.  The whole point of soft errors is to give people a choice:
> If it's expensive to modify code in your project, you can choose to
> ignore the errors, and your program will still mean what it used to
> mean.  That's "compatibility".  On the other hand, if you want to
> maximize readability, you can change all the uses of "Ditto" as an
> identifier to something else, and then start using "ditto" as a
> keyword.

That's what I meant by "Suppress". In Ada Standard terms, that's some sort
of pragma (either for Ignore or Suppress). Compilers might have other ways as
well (command line options, project files, etc.), but that's outside of what
the language can talk about. I called it "suppressed" since for most of them,
the default semantics is erroneous execution.

> You can phase in such changes over many months, if you like.
> These decisions should be up to Ada project managers, not ARG.

And that's fine if it isn't confusing to readers. But this case (and I mean
*specifically* this case) would be too confusing to readers.

> >...How is that any sort of advantage
> > over having it reserved?
>
> Ask Jeff.  Same reason as with "interface".

"Interface" is like "some" and "overriding" (if I was going to list all of
keywords that didn't need to be reserved, that would certainly be on the
list). It doesn't appear in expressions as a keyword.

Let me reiterate that I'm generally on your side on this issue, but I don't
agree with over-the-top arguments that *everything* ought to be done that way,
even at the cost of pages of rules about soft errors and the like.

> >...Plus you'd still have the danger of someone making  a compiler
> >which turns these warnings off (Robert used to threaten that about
> >GNAT and soft errors) and then the meaning changing silently.
>
> I've no idea what you mean.  If the RM requires a diagnostic message,
> then we can have an ACATS test that tests conformance with that rule.
> And of course one can write a nonconforming implementation of
> not-quite-Ada, and of course compilers can have switches that turn
> messages on and off.  So what?

The default for GNAT is the real defacto standard (sadly). When Robert
threatened to make the default for GNAT that soft errors are off, that pretty
much killed the idea for me. A soft error is an error that should be
suppressed only by a knowledgable user, who's made an explicit decision to
live with the consequences (hopefully because they've analyzed them). The
default for beginners is that they're errors.

Now, I realize the landscape has changed on that, but I still worry about
this. (And yes, the ACATS would check for the diagnostics *and* also check
the behavior when the error is suppressed or ignored or whatever it gets
called.)

> > I mean, this is the sort of thinking that gives non-reserved
> > keywords a bad name. In the cases of "overriding" and "some", the
> > keyword can only appear syntactically where no identifier is
> > allowed, so there can be no confusion and no need for any sort of warning.
>
> On the contrary, if nonreserved keywords existed, I think they deserve
> a warning.  And certainly the people who think nonreserved keywords
> are evil think that it's confusing to use a keyword as an identifier,
> no matter where in the syntax.  I would think soft errors would make
> those people more comfortable with the idea.

This I completely disagree with - in part because "those people" are wrong,
and I don't see much point in making them comfortable. The entire idea of
unreserved keywords should only be used in the case of keywords that exist
mainly to make the syntax more readable, and for those, there is no conflict
with declarations. I have parameters named "some", and I see no confusion
between those and the direction of a quantifier. Warnings would just be noise.

If there is a significant possible conflict, then the keywords should be
reserved. Period.

> >...So those make sense (even though we've  never been able to
> >convince enough people of that value). If the keyword can appear
> >somewhere a name can, the only possibility is confusion, no matter
> >what rules are adopted.
> >
> > > BTW, I don't much like "ditto".  I'd call it "it".  Or "lhs"
> > > is tolerable.
> > >
> > > Anyway, if nobody is seriously proposing a keyword (reserved or
> > > not), we don't need to settle these issues.
> >
> > Steve Michell has seriously proposed "ditto". Twice. I guess since
> > he's not an ARG member, he doesn't count?
>
> I can't keep track of who proposed what, nor how serious they are.
> I stand by my statement "IF (IF!) nobody is seriously proposing a
> keyword (reserved or not), we don't need to settle these issues."
> My statement doesn't say anything about who "counts".

Right, but why say "If False, we don't have to settle this?". It's a clear
no-op. The idea wouldn't have ever been discussed if someone wasn't seriously
suggesting it. (We surely don't need any straw men in this discussion, we have
plenty of wacky serious suggestions. :-)

If you had said, "Unless there is strong support for a keyword (reserved or
not), we don't need to settle these issues.", I wouldn't have complained.

(And I agree we don't have to settle these issues -- I was just wondering how
you could imagine that it ever would make sense to have a new keyword in an
expression unreserved. Which you've answered, so we both can shut up. :-)

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

From: Erhard Ploedereder
Sent: Tuesday, January 18, 2016  7:27 AM

>> Of all the existing proposals, I like @ or ?.
>> I hate "()" - too small a hamming distance to other programs.
>> (...) is read as "to be filled in later"
>> (.) is better but looks like Sumerian writing :-) .
>
> And what about "(..)" suggested by Brad, and preferred by myself, and
> I believe JP?

Well, (..) and (...) share this "to be filled in later" problem. If I wrote on
a slide in class:

A(F(X)) :=  B(Y) * (..);

99% of students would read this as "irrelevant detail omitted".

For

A(F(X)) := B(Y) * @;

they'd ask what the funny symbol stands for.

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

From: Jean-Pierre Rosen
Sent: Tuesday, January 19, 2016  7:54 AM

> For
>
> A(F(X)) := B(Y) * @;
>
> they'd ask what the funny symbol stands for.

And nobody could justify using this...

In my courses, I generally make fun at C for chosing '!' for "not", saying
that the only rationale for it was that it was not yet used on the keyboard.
Now the same would apply to Ada...

TBH, I'm afraid of very hostile reactions from the community. So let's be
clear about what I proposed: NOT to discuss with the public at large, but if
(as is likely) we have 2 or 3 proposals that are roughly on equal footing
amongst us, conduct a poll for user preference.

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

From: Brad Moore
Sent: Tuesday, January 19, 2016  9:23 AM

Here is yet another alternative, but it instantly appeals to me over other
suggestions, so it seems I should throw it out there.

Suppose we define an attribute, 'lhs   (short for left hand side),
where there prefix of the attribute is implicitly the current source line.

Really_Long_Name := 'lhs + 1;

Seems quite readable to me, and more Ada "friendlier" than

Really_Long_Name := @ + 1;

Similarly, extending Randy's example below

Long_Object_Name :=
   (if 'lhs in 1 .. 10 then 0 else 'lhs + Foo('lhs) - 10);

Seems more readable to me than

Long_Object_Name :=
   (if @ in 1 .. 10 then 0 else @ + Foo(@) - 10);

Also, I find that the @ in this example looks not that different than a zero
on my computer, the zeros and the @'s in the example kind of blend together, so
if its a sore thumb we're after, it feels like we need to hit it with a hammer
a couple times to make it stand out more.

I realize we dont have implicit prefixes yet in Ada, but perhaps we've not had
the need for one. In this case, resolving the entity for the lhs attribute is
always referring to a single known entity, the current source line.  Mentally
processing the examples above, I find the 'lhs provides a better mental
reminder of what is being substituted, whereas @ requires a double translation.
@ translates to lhs which translates to ...

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

From: Jean-Pierre Rosen
Sent: Tuesday, January 19, 2016  9:42 AM

> Here is yet another alternative
And another one: <:=>, where the <> carries the meaning "stands for", and ":="
means assignment ;-)

 (if <:=> in 1 .. 10 then <:=> else <:=> + Foo(<:=>) - 10);

Variants: <>:=   <:=

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

From: Randy Brukardt
Sent: Tuesday, January 19, 2016  2:27 PM

> > For
> >
> > A(F(X)) := B(Y) * @;
> >
> > they'd ask what the funny symbol stands for.
> And nobody could justify using this...
>
> In my courses, I generally make fun at C for chosing '!' for "not",
> saying that the only rationale for it was that it was not yet used on
> the keyboard. Now the same would apply to Ada...

Really? You don't think any of us will ever come up with an appropriate rationale??

> TBH, I'm afraid of very hostile reactions from the community.

Possibly, but that's *not* going to be because of the choice of syntax.
Ada's style is verbose, and this feature is all about our admitting that there
is such a thing as too much verbosity. Some people are not going to want to
admit that, and will be against any shorthand, regardless of syntax. (Just as
we've gotten people who are against conditional expressions; I've heard plenty
of hostility about those.)

For everyone else, it will not take long at all to get used to whatever
choice we make. And we need to make a choice that has the least possibility of
confusion. (But I think most of the people here are simply trying too hard --
this particular item has no short, natural notation, so we just have to choose
something and get used to it.)

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

From: Jean-Pierre Rosen
Sent: Tuesday, January 19, 2016  3:08 PM

Perhaps, but that's not my point. I'm just saying that if there is no clear
agreement between us, we can ask users what they prefer for syntax. I'm NOT
proposing to poll for or against the whole proposal.

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

From: Randy Brukardt
Sent: Thursday, April 21, 2016  9:55 PM

The reliability of Internet polls to decide serious questions is iffy at
best. Exhibit A: the British research vessel recently named "Boaty McBoatface"
by an Internet poll.

We'd probably get people trying to come up the most verbose possible syntax;
something like:
    Obj := <Rename_of_Target> + 1;
would likely win. :-) Well, really it would be a lot worse than that, but I
lack the imagination to come up with something that looks vaguely useful and
is also insanely long.

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

From: Jeff Cousins
Sent: Tuesday, March 8, 2016  8:38 AM

This might be of interest:

http://www.bbc.com/news/magazine-35744456

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

From: Tucker Taft
Sent: Tuesday, March 8, 2016  9:16 AM

My favorite name for "@" is "smudge" ;-)

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

From: Randy Brukardt
Sent: Thursday, April 21, 2016  10:32 PM

In filing the mail on the various threads on this AI, I'm struck by the sheer
number of ideas that this group has thrown out. At the risk of restarting a
discussion that has thankfully died out, I've gathered them all together.

Going back to Bennington, all of the following have been seriously proposed
for this idea (the first three are the only ones that ever had any significant
traction, the rest are in the order of occurrence -- I used a full example of
each so all of the nuances are visible):

  Obj01 := <<>> + 1;
  Obj02 := @ + 1;
  Obj03 := (..) + 1;

  Obj04 := [] + 1;
  Obj05 := $ + 1;
  Obj06 := # + 1;
  Obj07 := % + 1;
  Obj08 := <LHS> + 1;
  [Obj09] := [] + 1;
  {Obj10} := {} + 1;
  Obj11 := <-> + 1;
  Obj12 := <=< + 1;
  (Obj13) := () + 1;
  :Obj14: := :: + 1;
  (Obj15) := (..) + 1;
  (.Obj16.) := (..) + 1;
  Obj17 := ... + 1;
  temp renames Obj18 := temp + 1;
  Obj19 := (...) + 1;
  Obj20 := ditto + 1;
  Obj21 := -"- + 1;
  Obj22 := (.) + 1;
  Obj23! := ? + 1;
  Obj24 := it + 1;
  Obj25 := lhs + 1;
  Obj26 := .. + 1;
  Obj27 := 'lhs + 1;
  Obj28 := <:=> + 1;
  Obj29 := <>:= + 1;
  Obj30 := <:= + 1;


That's 30 distinct proposals, which is more than 2 per ARG member! (I admit,
I've proposed my fair share. :-)

(And of course, this discussion goes back long before Bennington, but I can't
justify reading years of old mail - even though it might be more fun than
work. :-)

If you want to see why any of these were proposed, or who was responsible, or
what the objections were, I refer you to the mail filed in AI12-0125-3.

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

From: Tucker Taft
Sent: Friday, April 22, 2016  7:27 AM

> Going back to Bennington, all of the following have been seriously
> proposed for this idea (the first three are the only ones that ever
> had any significant traction, the rest are in the order of occurrence
> -- I used a full example of each so all of the nuances are visible):

And my own personal favorite:

    Obj00 :+ 1;

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

From: Randy Brukardt
Sent: Friday, April 22, 2016  2:14 PM

As I noted, I didn't go back to ideas proposed before Bennington (and there
were many of those, too). It would have been fun to do so, but not a good use
of time.

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

From: Randy Brukardt
Sent: Tuesday, August  2, 2016  12:09 AM

I haven't actually worked on this AI yet (so there might be something else
later), but I realized that I forgot something when I originally defined this
AI. There is a list of "delimiters" (really single character lexical symbols)
in 2.2(9). Clearly, we have to add '@' to that list. It should be added as the
second-last item, as this list is in ASCII code order (and @ follows > and
precedes | in that encoding).

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

From: Jeff Cousins
Sent: Tuesday, August  2, 2016  5:41 AM

Well spotted.

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

From: Randy Brukardt
Sent: Friday, August  5, 2016  1:22 AM

As I guessed, there would be more.

The otherwise brilliant author of this AI (that would be me) forgot to provide
examples for this new subclause. It seems rather important to do so, as the
formal definition is rather inscrutable.

Of course, RM examples have to be based on previous declarations in the RM, so
all of the existing examples in the AI are no good. I propose that we use the
following:

Examples

    Board(1, 1) := @ + 1;  -- An abbreviation for Board(1, 1) := Board(1, 1) + 1;
                           -- (Board is declared in 3.6.1).

    Long_Ago : Date := Yesterday; -- See 3.8
    Long_Ago := (Year  => @.Year - 1,
                 Month => (if @.Month = January then January else Month_Name'Pred(@.Month)),
                 Day   => (if @.Day = 1 then 28 else @.Day - 1));
       -- A target_name can be used multiple times and as a prefix if needed.

If you have an improvement, please suggest it (there's not many exciting types
declared in chapter 3 or 4).

[Note: Month_Name is never actually defined in the RM, 4.3.1 uses it however
and uses the full month names so I followed suit here. I suppose Jeff will ask
me to add Month_Name to the examples in 3.5.1.]

---

In other news, I marked the entire Name Resolution Paragraph as redundant,
given that the first sentence is formally defined in 8.6 (that was already in
the AI), the first half of the second sentence is given in 3.3, and the
second half of the second sentence is given in the Static Semantics
equivalence.

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

From: Tucker Taft
Sent: Friday, August  5, 2016  9:30 AM

> Examples
>
>     Board(1, 1) := @ + 1;  -- An abbreviation for Board(1, 1) :=
> Board(1, 1)
> + 1;
>                            -- (Board is declared in 3.6.1).
>
>     Long_Ago : Date := Yesterday; -- See 3.8
>     Long_Ago := (Year  => @.Year - 1,

You need to add a comment here to explain the goal of this -- not obvious on a
first reading.  In fact not clear after several readings.  Are you going to
the prior year, month, and day?  What if it is January 1st?  Don't you want
to subtract two from Year then?

>                  Month => (if @.Month = January then January else Month_Name'Pred(@.Month)),

I presume you mean "(if @.Month = January then December else ...)"

>                  Day   => (if @.Day = 1 then 28 else @.Day - 1));
>        -- A target_name can be used multiple times and as a prefix if needed.
>
> If you have an improvement, please suggest it (there's not many
> exciting types declared in chapter 3 or 4).

Your "Long_Ago" example is interesting but inscrutable at the moment.

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

From: Randy Brukardt
Sent: Friday, August  5, 2016  3:22 PM

...
> You need to add a comment here to explain the goal of this -- not
> obvious on a first reading.

The goal is to provide an example - it doesn't do anything meaningful.

...
> > If you have an improvement, please suggest it (there's not many
> > exciting types declared in chapter 3 or 4).
>
> Your "Long_Ago" example is interesting but inscrutable at the moment.

There's a reason I asked for suggestions!! :-)

The original idea was just to subtract a year:

   Last_Year := (Year => @.Year-1, Month => @.Month, Day => @.Day);

But that probably would be better done with a delta aggregate (and we haven't
approved those yet):

   Last_Year := (@ with delta Year => @.Year-1);

or just directly as:

   Last_Year.Year := @ - 1;

which is of course the first example all over again.

So I changed the basic idea to subtracting a year, month, and day. But led to
deciding what to do with the underflow (if already the first month or day;
don't have to worry about that for the year since "Yesterday" is never going
to have a year near the lower bound). Properly dealing with that requires
carrying results and isn't practical in a one-liner (it's a complex series of
nested ifs). So I just punted.

I also wanted to complicate the target, but that also complicated the
example:

    Dates : array (1 .. 10) of Date := (others => Yesterday);
    for Idx in Dates'Range loop
        case Idx mod 3 is
            when 0 => -- Subtract a year:
		    Dates(Idx) := (Year => @.Year-1, Month => @.Month, Day => @.Day);
            when 1 => -- Subtract a month:
		    Dates(Idx) := (Year  => (if @.Month = January then @.Year-1 else @.Year),
                               Month => (if @.Month = January then December else Month_Name'Pred(@.Month)),
                               Day   => @.Day);
            when 2 => -- Subtract a day:
                -- Left as an exercise for the reader!!!
        end case;
    end loop;

Humm. Maybe "Subtract a month" is just complex enough for this problem, not
so simple that there is a better solution and not so complex as to drive one
nuts. So maybe we should use:

     Last_Month := (Year  => (if @.Month = January then @.Year-1 else @.Year),
                    Month => (if @.Month = January then December else Month_Name'Pred(@.Month)),
                    Day   => @.Day);

Or maybe:

    Dates : array (1 .. 10) of Date := ...;
    -- Subtract a month from each:
    for Idx in Dates'Range loop
       Dates(Idx) := (Year  => (if @.Month = January then @.Year-1 else @.Year),
                      Month => (if @.Month = January then December else Month_Name'Pred(@.Month)),
                      Day   => @.Day);
    end loop;

Thoughts??

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

From: Tucker Taft
Sent: Friday, August  5, 2016  4:07 PM

> ... Humm. Maybe "Subtract a month" is just complex enough for this
> problem, not so simple that there is a better solution and not so
> complex as to drive one nuts. So maybe we should use:
>
>      Last_Month := (Year  => (if @.Month = January then @.Year-1 else
> @.Year),
>                     Month => (if @.Month = January then December else
> Month_Name'Pred(@.Month)),
>                     Day   => @.Day);

This seems about right, modulo formatting ;-).

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

From: Randy Brukardt
Sent: Friday, August  5, 2016  7:16 PM

OK, I'll go with that (and the formatting was fine when I wrote it ;-),
modulo comments from others.

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

From: Jeff Cousins
Sent: Tuesday, August  9, 2016  10:53 AM

Of the examples, I prefer the last.
Though to emphasise that @ can be short-hand for something quite complex, I'd
put more complexity on the left hand side and less on the right, though still
leaving the right hand side complex enough to have two @s, to still show that
@ can be used multiple times:

    Dates : array (1 .. 10) of Date := ...;
    -- Previous month for each (wrapping round):
    for Idx in Dates'Range loop
       Dates(Idx).Month :=
          (if @ = January then December else Month_Name'Pred(@));
    end loop;

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

From: Randy Brukardt
Sent: Tuesday, August  9, 2016  3:19 PM

That suggestion doesn't cover my primary objective for the second example, which
is to show that @ can be used as a name (specifically as a prefix to a selected
component). I didn't realize that myself, and it seems important. Multiple uses
was a secondary objective; I was happy to get both into a single example. (Along
with showing a case where there is no chance of using something like :=+.)

I could see using the last as it was originally written, or perhaps there is
some other version that would cover all of the objectives.

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

From: Jeff Cousins
Sent: Wednesday, August 10, 2016  3:07 AM

Ok, may be add "@ can be used as a name" as a comment.

I still prefer the last example to the penultimate one, Last_Month seems too
trivial for the left hand side, I'd like something with a bit of address
calculation (i.e. Dates(Idx)).

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

From: Randy Brukardt
Sent: Wednesday, August 10, 2016  9:36 PM

Since Tucker and Jeff seem to disagree on which example to use in the user
Examples section of the RM for the new Target Name Symbol subclause, and it
would be silly to reopen the AI for such a thing, I am asking for input from the
entire ARG. (If that fails to find a resolution, I'll have to create an AI just
for this question. Bah.)

A reminder of the requirements for this RM example:
  (1) The example has to be complete or depend on declarations earlier in the
      RM;
  (2) The example has to show the use of multiple target name symbols in a
      single assignment;
  (3) The example has to show the use of a target name symbol as a name (that
      is, as a prefix rather than standing alone);
  (4) The example should be something realistic and understandable.

The current candidates:

[A] (Tucker's choice)

     Last_Month : Date := Yesterday; -- See 3.8
     Last_Month := (Year  => (if @.Month = January then @.Year-1 else @.Year),
                    Month => (if @.Month = January then December else Month_Name'Pred(@.Month)),
                    Day   => @.Day);
        -- A target_name can be used multiple times and as a prefix if needed.

[B] (Jeff's choice)

    Dates : array (1 .. 10) of Date := ...;  -- See 3.8
    -- Subtract a month from each:
    for Idx in Dates'Range loop
       Dates(Idx) := (Year  => (if @.Month = January then @.Year-1 else @.Year),
                      Month => (if @.Month = January then December else Month_Name'Pred(@.Month)),
                      Day   => @.Day);
        -- A target_name can be used multiple times and as a prefix if needed.
    end loop;

[C] (An alternative to B, using a declaration from 3.3.1 instead of the
loop)

    Dates : array (1 .. 10) of Date := ...;  -- See 3.8
    Dates(Count) :=                          -- See 3.3.1
          (Year  => (if @.Month = January then @.Year-1 else @.Year),
           Month => (if @.Month = January then December else Month_Name'Pred(@.Month)),
           Day   => @.Day);
        -- A target_name can be used multiple times and as a prefix if needed.

Please vote for one.

P.S. If you want to suggest something different, please ensure that the
suggestion meets (1) through (4) above.

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

From: Jeff Cousins
Sent: Thursday, August 11, 2016  4:15 AM

C, preferably with the addition of comment     -- Subtract a month

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

From: Tucker Taft
Sent: Thursday, August 11, 2016  7:52 AM

I vote for C but change "1 .. 10" to "1 .. Max" (Max is declared in 3.3.2).
It just seems weird to use a literal high bound and then use a non-literal index
that comes out of thin air.

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

From: Bob Duff
Sent: Thursday, August 11, 2016  8:37 AM

C.

And I agree with Jeff's and Tucker's comments.

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

From: Brad Moore
Sent: Thursday, August 11, 2016  8:40 AM

I think there is a problem with all of these examples, in that this blows up if
the day number doesn't exist in the previous month, and doesn't handle leap
years in the case of March 31.

My vote is for [A], except change Last_Month to Start_Of_Last_Month,and then set
the Day component to 1 on the assignment.

To me this rules out the other choices because [A] is simpler, and the variable
name describes the intent. Adding arrays and loops just obfuscates the example.

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

From: Jeff Cousins
Sent: Thursday, August 11, 2016  8:50 AM

> ... Adding arrays and loops just obfuscates the example.

I think it's important that the left hand side is something more than a simple
variable.

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

From: Steve Baird
Sent: Thursday, August 11, 2016  12:13 PM

> I think there is a problem with all of these examples, in that this
> blows up if the day number doesn't exist in the previous month, and doesn't handle leap years in the case of March 31.

I agree. We don't want to showcase a sloppy example.

Modulo that, I agree with Bob (i.e., C and agreement with Jeff's and Tucker's
comments).

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

From: Tullio Vardanega
Sent: Thursday, August 11, 2016  1:37 PM

I am for [C], after fixing the logic of the month decrement operation, which is
currently broken.

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

From: Randy Brukardt
Sent: Thursday, August 11, 2016  10:34 PM

> > I think there is a problem with all of these examples, in that this
> > blows up if the day number doesn't exist in the previous month, and
> > doesn't handle leap years in the case of March 31.
>
> I agree. We don't want to showcase a sloppy example.
>
> Modulo that, I agree with Bob (i.e., C and agreement with Jeff's and
> Tucker's comments).

Huh? There is no way to repair C and both meet these additional requirements and
meet the original requirements as well. As such, I withdraw the example
completely -- I don't need the stress of this -- and -- in the absence of
someone else proposing an example or examples that fulfill all of the
requirements -- will ask that the entire AI be reconsidered. (If we can't find a
realistic example, then the entire idea is dubious.)

[I'll give a detailed breakdown of my thoughts below, but I need everyone to
read the first part of this, so it will come later.]

To repeat the requirements, as added to by various people here:

  (1) [Std Reqmnt] The example has to be complete or depend on declarations
      earlier in the RM;
  (2) [Randy's intent] The example has to show the use of multiple target name
      symbols in a single assignment;
  (3) [Randy's intent] The example has to show the use of a target name symbol
      as a name (that is, as a prefix rather than standing alone);
  (4) [Tucker's addition] The example should be something realistic and
      understandable;
  (5) [Jeff's addition] The example's target name [LHS] needs to be something
      complex (preferably some array indexing);
  (6) [Brad's addition, 2nded by others] The example must not be "sloppy"; there
      cannot be any corner cases unhandled.

My problem is that no new subclause in the standard should exist without user
examples. Since I'm apparently not allowed to have a reasonably simple example,
my first inclination is to send the AI back the ARG until someone proposes it.
However, doing so would cost me 4-6 hours of work since I've already put the AI
into the draft Standard (2 hours to take it out -- it appears in 6 different
chapters -- and 2-4 hours to put it back at some future date). That seems like a
massive waste of my time and AdaCore's money. Similarly, making a separate AI
for the examples would also cost several hours of work (I'd have to remove the
examples from the draft Standard, and then presumably put some back at a future
date, as well as create a draft AI).

So, please propose an example (*not* using dates; it's obvious that we can never
find an example using those that meets everyone's requirements); else I'm going
to have to spend a lot of time doing busywork rather than anything valuable.

====================================

Here's my detailed rant on this topic. If you want to know why I'm both upset
and at a loss as how to proceed, read on. If you are in a hurry, feel free to
skip the below.

[A] I'm certain that the date example cannot be reasonably fixed. The intended
operation (find the same date in the previous month) is commonly used (well, at
least the operation in the *next* month is), for instance in my financial
software (find the default date for next month's statement; it's the same as the
date of this month's statement.

We could fix the major problem with a table of month-ends (I changed the example
to next month to be more realistic):

    Dates : array (1 .. Max) of Date := ...;  -- See 3.8 and 3.3.2
    Month_End : array (Month_Name) of Natural := (January => 31, February => 28, ...);
    -- Find the same date next month:
    Dates(Count) :=                           -- See 3.3.1
          (Year  => (if @.Month = December then @.Year+1 else @.Year),
           Month => (if @.Month = December then December else Month_Name'Succ(@.Month)),
           Day   => (if @.Month = December then @.Day else
                        (if @.Day > Month_End(Month_Name'Succ(@.Month)) then
                              Month_End(Month_Name'Succ(@.Month)) else @.Day)));
        -- A target_name can be used multiple times and as a prefix if needed.

However, leap years are essentially insolvable in a one-liner for this type, as
the years run from 0 .. 4000 and the algorithm is different for years prior to
1600. Moreover, this would make the example insanely complex, which defeats the
purpose.

The other realistic example, adding or subtracting a number of days (say 30),
has all of the same problems, so I won't consider it further.

[B] Brad's suggestion of making the date the first unconditionally fails any
sort of "realistic" requirement. One could imagine (barely) doing that into a
local variable, but not with an array as such an operation discards information.
It's a non-starter for me.

[C] The remaining problem here is that any "realistic" code would have Date as
an ADT of some sort, so no one would ever write any of these examples as a
one-liner anyway. If we're really going to insist on a "realistic" example that
isn't "sloppy", we can't use dates at all.

[D] I don't believe that many of the other examples in the RM meet (4) or (6).
So I don't quite understand why this one is getting treated differently. For
instance, calling the interface examples in 3.9.4 "realistic" and not "sloppy"
is a stretch. (The next time I write a routine like Transfer will be my first --
and what happens if the From queue doesn't have Number elements? How is that
realistic?) And 3.9.4 is probably one of the better examples.

[E] I briefly thought about trying some sort of matrix operation, but the array
aggregate syntax is horrible enough to hide what's going on:

    Tuple : Vector (1 .. 3) := ...;             -- See 3.6.
    Matrix_List : array (1 .. Max) of Matrix(1 .. 3, 1 .. 3) := ...; -- See 3.3.2, 3.6.
    -- Multiply by Tuple:
    Matrix_List(Count) :=                -- See 3.3.1.
        (1 => (1 => @(1,1)*Tuple(1), 2 => @(1,2)*Tuple(1), 3 => @(1,3)*Tuple(1)),
         2 => (1 => @(2,1)*Tuple(2), 2 => @(2,2)*Tuple(2), 3 => @(2,3)*Tuple(2)),
         3 => (1 => @(3,1)*Tuple(3), 2 => @(3,2)*Tuple(3), 3 => @(3,3)*Tuple(3)));
        -- A target_name can be used multiple times and as a prefix if needed.

This also has the same objection described in [C]: no one would ever write a
matrix multiply this way. And it's "sloppy" in that doing an operation this way
is likely to have numeric problems.

Perhaps the new for syntax would help:

    Tuple : Vector (1 .. 3) := ...;             -- See 3.6.
    Matrix_List : array (1 .. Max) of Matrix(1 .. 3, 1 .. 3) := ...; -- See 3.3.2, 3.6.
    -- Multiply by Tuple:
    Matrix_List(Count) :=                -- See 3.3.1.
        (for Row in 1 .. 3 => (for Col in 1 .. 3 => @(Row,Col)*Tuple(Col)));
        -- A target_name can be used multiple times and as a prefix if needed.

I suppose it does, but then we're depending on another AI (not great), and this
does nothing to eliminate the [C] objections or the sloppiness. (I also can't
figure out if I have Row and Col right, which doesn't bode well for using this
in practice. :-)

[F] I give up at this point. I'm fairly convinced that no example meeting (1) ..
(6) exists. Indeed, even ignoring (1), I think that any case where I've written
something like this in practice would have been described as "sloppy"; the
operation should have been written as an ADT (in which case Jeff's requirement
would not be met inside of the ADT operation, and there'd be nothing but a
subprogram call at the point of use). Any example has to be either non-realistic
or sloppy or both.

[G] This latter realization is what makes me wonder about the wisdom of the
feature at all (at least in this level of generality). It seems to mainly be
valuable to support "sloppy" code, outside of single, stand-alone uses. Should
this really be a name, or would it be better to make it a primary?? Do we really
need to allow multiple uses? In the absence of an example, I'm starting to lean
toward reconsideration.

[H] Someone else needs to take a crack that this, because I'm not getting
anywhere, but I am managing to waste vast amounts of time.

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

From: Jean-Pierre Rosen
Sent: Friday, August 12, 2016  2:37 AM

> Huh? There is no way to repair C and both meet these additional
> requirements and meet the original requirements as well. As such, I
> withdraw the example completely -- I don't need the stress of this --
> and -- in the absence of someone else proposing an example or examples
> that fulfill all of the requirements -- will ask that the entire AI be
> reconsidered. (If we can't find a realistic example, then the entire
> idea is dubious.)
[rant deleted]

Seems to me that if you call the variable "Due_Date", then it makes reasonable
sense for it to be the first day of next month, and all the problems go away...

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

From: Tucker Taft
Sent: Friday, August 12, 2016  7:13 AM

That seems like a clever idea.

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

From: Jeff Cousins
Sent: Friday, August 12, 2016  8:05 AM

That seems reasonable.

Or using the other type in 3.8, how about

   My_Complex_Array : array (1 .. Max) of Complex; -- 3.3.2, 3.8

begin

   -- Square first element

My_Complex_Array (1) := (Re => @.Re**2 - @.Im**2,
                         Im => -2.0 * @.Re * @.Im);

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

From: Tucker Taft
Sent: Friday, August 12, 2016  9:55 AM

Here is the example with JP's suggestion (and mine):


     Due_Dates : array (1 .. Max) of Date := ...;  -- See 3.3.2, 3.8
     Due_Dates(Count) :=                          -- See 3.3.1
           (Year  => (if @.Month = December then @.Year+1 else @.Year),
            Month => (if @.Month = December
                      then January else Month_Name'Succ(@.Month)),
            Day   => 1);  -- Adjust to be first of following month

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

From: Erhard Ploedereder
Sent: Friday, August 12, 2016  10:44 AM

I like this version best for (C).

Actually, I preferred the loop over Count with a reference that is completely
disappointing when followed, but I saw from the polls that this is a minority
opinion.

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

From: Brad Moore
Sent: Friday, August 12, 2016  11:12 AM

This works for me also.

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

From: Tullio Vardanega
Sent: Friday, August 12, 2016  1:25 PM

I am also happy with this version.

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

From: Randy Brukardt
Sent: Friday, August 12, 2016  8:06 PM

> >> ... Seems to me that if you call the variable "Due_Date", then it
> >> makes reasonable sense for it to be the first day of next month,
> >> and all the problems go away...
> >
> > That seems like a clever idea.

I find it at least an unrealistic as the earlier ideas. And this basic idea
(Brad's, originally) has made me quite upset; I had a lot of trouble getting to
sleep last night.

> Here is the example with JP's suggestion (and mine):
>
>
>      Due_Dates : array (1 .. Max) of Date := ...;  -- See 3.3.2, 3.8
>      Due_Dates(Count) :=                          -- See 3.3.1
>            (Year  => (if @.Month = December then @.Year+1 else @.Year),
>             Month => (if @.Month = December
>                       then January else Month_Name'Succ(@.Month)),
>             Day   => 1);  -- Adjust to be first of following month

We'll use that over my dead body. It's far less realistic than the earlier
ideas, and at least as sloppy.

I've spent quite a bit of the time since J-P originally posted his idea figuring
out precisely why this particular idea has this effect on me. I think I've
figured it out well enough, but better yet I've figured out a better example
which avoids the worst of the problems.

[A] My original thought was that the operation is trying to convert an arbitrary
billing date to a due date. (That's by far the most common case.) But that is
almost always a fixed number of days ahead - 10 or 15 or 25 or 30 days from the
billing date. We've already looked at and rejected that as an example, as
month-end and leap year handling is a nightmare (it certainly is in
Ada.Calendar.Arithmetic!).

[B] The alternative is to have a contractual due date that doesn't change.
That's much rarer in practice, and many of those adjust to avoid Sundays and
holidays. Moreover, those have no relationship to the arbitrary billing date, so
it doesn't make sense to even calculate one from the other.

[C] Clearly, the problem I'm having is that the usage case is ill-defined and
seems totally arbitrary. The objection to my suggestions seems to have mainly
been that the usage case wasn't well-defined, and the same has to apply to the
suggestions made by others. As it is, I feel I'm being held to a much higher
standard, and that is what rankles the most.

[D] Let's for a moment assume that you somehow have a contractual arrangement
that is always due on the first. First of all, the use of a magic number as in
the above example is always discouraged, and should be replaced by a constant
(what if the law changes?). So that's unrealistic.

[E] Moreover, the use of a day at all doesn't make sense in this "single date"
context. Why would anyone store a value that never changes? That would be a
massive waste of space (especially if this is for a large health insurer with
very many customers). You'd use a type that just included the year and month. (A
number of the bills I get are structured exactly that way; the bill itself isn't
dated at all, it's just for a month.) Alternatively, you might use an existing
time/date type. The one thing you would never do is define a custom type with a
date component and then not use it for anything realistic.

[F] There has to be a grace period in the dates in any realistic scenario; if
you're figuring out the first due date of a new contract, the first payment
isn't due until some time after signing. It would be madness to have a contract
signed on July 31st require a payment due on August 1st.

[G] Ergo, the Due_Dates array cannot contain arbitrary dates; they have to be
limited in some way. A better scenario (IMHO) is to define the array to hold the
Next_Due_Date, which is limited to hold only legitimate due dates. (In practice,
one would want to enforce that with a predicate, but that's outside of this
example.) Then the scenario would be to advance the due date because a full
payment was successfully received.

[H] But of course, if we restrict the values in the Next_Due_Date array, the
previous version of the example works just as well. Just make sure that the
restriction doesn't allow any day number over 28.

[I] So I propose the following example:

      Next_Due_Date : array (1 .. Max) of Date := ...;  -- See 3.3.2, 3.8
          -- Customer payments are due on the 1st or 15th of the month.
      ...
      -- Customer Count (see 3.3.1) has paid in full this month; advance the due date:
      Due_Dates(Count) :=
            (Year  => (if @.Month = December then @.Year+1 else @.Year),
             Month => (if @.Month = December
                       then January else Month_Name'Succ(@.Month)),
             Day   => @.Day);

      AARM Discussion: We're taking advantage of the fact that the Day can only be 1 or 15
      to simplify the calculation. If the Day could be late in the month, one would have to
      adjust for short months and leap years.

It would be better if the index was named Customer or something like that, but
we don't have an example like that.

I tend to agree with Erhard that the use of Count is annoying, because it
doesn't really have much relation to the problem. But a for loop doesn't make
much sense in this scenario, so I'm not proposing that.

[J] This example is still unrealistic in that any sane programmer would create a
function to do the above, at least to avoid having to retest the conditions
multiple times. I'd probably write something like:
      function Next_Month (The_Date : Date) return Date is
      begin
          return Result : Date := The_Date do
             if Result.Month < December then
                 Result.Month := Month_Name'Succ(Result.Month);
             else
                 Result.Month := January;
                 Result.Year := @ + 1;
             end if;
             -- Adjust day for month-end here.
         end return;
      end Next_Month;

which is both easier to understand and reusable.

As such, an example not involving Date would be preferable.

However, in the absence of a better suggestion, I'll use the example given in
[I]. Let's see if you guys can keep me from sleeping the entire weekend...

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

From: Brad Moore
Sent: Sunday, August 14, 2016  1:14 PM

> As such, an example not involving Date would be preferable.

How about this?

Locations : array (1 .. Max) of Painted_Point := ...;  -- See 3.3.2, 3.9.1, 3.9

-- Shift all locations to the right
for Pos in Locations'Range loop
   Locations(Pos) := Painted_Point'(X => @.X + 1.0, Y => @.Y, Paint => @.Paint); end loop;

This seems more realistic, where the Painted points are absolute locations, and
one wants to update all the locations by a relative offset.

It also involves a loop, which some mentioned they would prefer to see.

> However, in the absence of a better suggestion, I'll use the example
> given in [I]. Let's see if you guys can keep me from sleeping the
> entire weekend...

I thought it would be best to let you sleep in on Sunday, assuming you'd be
having to get up earlier on Monday anyways.

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

From: Randy Brukardt
Sent: Monday, August 15, 2016  7:33 PM

> It also involves a loop, which some mentioned they would prefer to
> see.

One minor problem with it, however, as written it would make more sense to just
update the individual component. The example probably should involve multiple
components. That's easy enough to do (see the next message).

> > However, in the absence of a better suggestion, I'll use the example
> > given in [I]. Let's see if you guys can keep me from sleeping the
> > entire weekend...
>
> I thought it would be best to let you sleep in on Sunday, assuming
> you'd be having to get up earlier on Monday anyways.

Thanks, I think. :-)

I know that it looks like I'm taking this example too seriously, but since these
examples are likely to be the first exposure of Ada users (new and old) to this
feature, I think it's important to get them right.

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

From: Randy Brukardt
Sent: Monday, August 15, 2016  7:35 PM

...
> Or using the other type in 3.8, how about
>
>   My_Complex_Array : array (1 .. Max) of Complex; -- 3.3.2, 3.8 begin
>   -- Square first element
>   My_Complex_Array (1) := (Re => @.Re**2 - @.Im**2,
>                            Im => -2.0 * @.Re * @.Im);

Some Jeff guy wanted the LHS to be more complex; it probably should use a
variable (perhaps Count again). Otherwise looks fine.

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

From: Randy Brukardt
Sent: Monday, August 15, 2016  7:45 PM

Thanks guys, now I have too many examples, and I don't have a clear winner in
the bunch. So let me ask for preferences.

First, let me remind you of the simple example (not in question here):

   Board(1, 1) := @ + 1;  -- An abbreviation for Board(1, 1) := Board(1, 1) + 1;
                          -- (Board is declared in 3.6.1).

The question is what should follow this. We have three suggestions (in no
particular order):

(A)
    My_Complex_Array : array (1 .. Max) of Complex; -- 3.3.2, 3.8
    ...
    -- Square the element in the Count (see 3.3.1) position:
    My_Complex_Array (Count) := (Re => @.Re**2 - @.Im**2,
                                 Im => -2.0 * @.Re * @.Im);
       -- A target_name can be used multiple times and as a prefix if needed.
(B)
    Next_Due_Date : array (1 .. Max) of Date := ...;  -- See 3.3.2, 3.8
        -- Customer payments are due on the 1st or 15th of the month.
    ...
    -- Customer Count (see 3.3.1) has paid in full this month; advance the due date:
    Due_Dates(Count) :=
         (Year  => (if @.Month = December then @.Year+1 else @.Year),
          Month => (if @.Month = December
                    then January else Month_Name'Succ(@.Month)),
          Day   => @.Day);
       -- A target_name can be used multiple times and as a prefix if needed.

      AARM Discussion: We're taking advantage of the fact that the Day can only be 1 or 15
      to simplify the calculation. If the Day could be late in the month, one would have to
      adjust for short months and leap years.

(C)
    Locations : array (1 .. Max) of Painted_Point := ...;  -- See 3.3.2, 3.9.1, 3.9

    -- Shift all locations down and to the right:
    for Pos in Locations'Range loop
       Locations(Pos) := Painted_Point'(X => @.X + 1.0, Y => @.Y + 1.0, Paint => @.Paint);
           -- A target_name can be used multiple times and as a prefix if needed.
    end loop;

[Aside: Do we need the qualification here? If not, I'd rather drop it to
simplify the example. Painted_Point is tagged.]

Please indicate your preference and any comments. (In the next day or so in
order that I can put this AI and the rest out for editorial review.)

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

From: Jeff Cousins
Sent: Tuesday, August 16, 2016  4:41 AM

A, but I'd be ok with any of them.
C doesn't need qualification.

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

From: Randy Brukardt
Sent: Tuesday, August 16, 2016  10:23 PM

So far, Jeff has voted for [A] (not surprising, it was his suggestion :-).
Anyone else have a preference???

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

From: Jean-Pierre Rosen
Sent: Wednesday, August 17, 2016  3:02 AM

I can definitely live with all three. (Slight) preference order:
B, A, C

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

From: Tullio Vardanega
Sent: Wednesday, August 17, 2016  3:24 AM

I concur with JP:
- can live with all three
- my preference order is: B, A, C

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

From: Tucker Taft
Sent: Wednesday, August 17, 2016  6:04 AM

All are fine.  In order, my preferences are [A], [B], [C].

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

From: Brad Moore
Sent: Wednesday, August 17, 2016  8:34 AM

All fine, my preferences are [A]. [C], [B] Brad

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

From: Erhard Ploedereder
Sent: Wednesday, August 17, 2016  8:35 AM

I go for [a]. Teaches not only Ada but also Math.

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

From: Gary Dismukes
Sent: Wednesday, August 17, 2016  12:35 PM

I'm also in favor of 'a'.

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

From: Randy Brukardt
Sent: Thursday, August 18, 2016  8:33 PM

Thanks, guys. We have a clear winner: six people (including me) selected A as
their first choice, and the other two put it down as their second choice. B got
two first place votes, one second place vote, and two third place votes, and the
rest didn't mention it at all. Poor C was hardly mentioned at all (two seconds
and three thirds).

I'll fix up everything to use A and we can consider this matter closed.

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

From: Jeff Cousins
Sent: Friday, August 19, 2016  2:20 AM

Thanks. But is A actually correct? Why did I put a - before the 2.0?

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

From: Randy Brukardt
Sent: Friday, August 19, 2016  2:43 PM

After wasting 20 minutes reading articles about complex numbers, I've concluded
that you are right that you are wrong. :-)

Wikipedia gives the formula for multiplication as:

  (a + bi)*(c + di) = (ac - bd) + (ad + bc)i

If a = c and b = d, then we get
   a**2 - b**2 + 2.0*a*b*I

So you have an extra minus sign in your example.

(Most of the hits I got to the query "square complex number" finds articles about
something called an "absolute square" which is something different -- it's real
valued (not complex).)

I'll fix this.

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

From: Jeff Cousins
Sent: Friday, August 19, 2016  3:01 PM

Thanks Randy. Maybe we should all go back to school to revise our math(s)!

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

From: Erhard Ploedereder
Sent: Saturday, August 20, 2016  6:34 PM

> Thanks. But is A actually correct? Why did I put a - before the 2.0?

And I thought that this was the overloaded "-" to implement the idempotent
flyspeck operator for screens.

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

Questions? Ask the ACAA Technical Agent