!standard 5.2.1(0) 16-08-05 AI12-0125-3/05 !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 10-0-2 16-06-12 !status work item 15-10-13 !status received 15-09-22 !priority Low !difficulty Medium !subject Add @ as a abbreviation for the LHS of an assignment !summary Define @, which can be used in the right-hand side of an assignment statement as a 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 anonynous 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) 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. 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 : renames Integer := Obj.I; -- (3) begin := + 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) @drepl & @ @ ' @ @ ( @ @ ) @ @ * @ @ + @ @ , @ @ @endash @ @ . @ @ / @ @ : @ @ ; @ @ < @ @ = @ @ @> @ @ | @dby & @ @ ' @ @ ( @ @ ) @ @ * @ @ + @ @ , @ @ @endash @ @ . @ @ / @ @ : @ @ ; @ @ < @ @ = @ @ @> @ @ @@ @ @ | !corrigendum 3.3(21.1/3) @dinsa @xbullet;> @dinst @xbullet of an @fa when used in the @fa of the assignment (see 5.2.1);> !corrigendum 4.1(2/3) @drepl @xcode<@fa@fa< ::=> @fa | @fa | @fa | @fa | @fa | @fa | @fa | @fa | @fa | @fa | @fa | @fa> @dby @xcode<@fa@fa< ::=> @fa | @fa | @fa | @fa | @fa | @fa | @fa | @fa | @fa | @fa | @fa | @fa | @fa> !corrigendum 5.2.1(0) @dinsc @@, known as the @i of an assignment statement, provides an abbreviation to avoid repetition of potentially long names in assignment statements. @s8<@i> @xindent<@fa@fa< ::= >@@> @s8<@i> If a @fa occurs in an @fa @i, the @i@fa @i of @i is a complete context. The target name is a constant view of @i, having the nominal subtype of @i. @s8<@i> A @fa shall only appear in the @fa of an @fa. @s8<@i> The @i@fa is evaluated only once. In particular, if a @fa with nominal subtype @i appears in the @fa of an @fa @i, then @i is equivalent to a call on a local anonymous procedure with the actual parameter being the @i@fa of @i, where the local anonymous procedure has an @b parameter with unique name @i

of subtype @i, with a body being @i with the @i@fa being replaced by @i

, and any @fas being replaced by the qualified expression @i'(@i

). @s8<@i> @xcode> -- @ft<@i<(Board is declared in 3.6.1).>>> @xcode> Last_Month := (Year =@> (@b @@.Month = January @b @@.Year - 1 @b @@.Year), Month =@> (@b @@.Month = January @b December @b Month_Name'Pred(@@.Month)), Day =@> @@.Day); -- @ft<@i>> !corrigendum 8.6(9/4) @dinsa @xbulletexpression of a @fa or @fa.> @dinst @xbullet@fa of an @fa @i, if the @fa of @i contains one or more @fas.> !ASIS ** TBD ** !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 := + 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 := + 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.) ****************************************************************