CVS difference for ai12s/ai12-0257-1.txt

Differences between 1.3 and version 1.4
Log of other versions for file ai12s/ai12-0257-1.txt

--- ai12s/ai12-0257-1.txt	2020/01/31 05:51:39	1.3
+++ ai12s/ai12-0257-1.txt	2020/03/23 03:44:17	1.4
@@ -2537,3 +2537,352 @@
 somewhere.]
 
 ****************************************************************
+
+From: Tucker Taft
+Sent: Wednesday, March 11, 2020  8:02 PM
+
+I have been reviewing a submission to Ada-Europe and they make a pretty 
+compelling argument for adopting the "Uniform Function Call Syntax" (of the 
+"D" language, aka "UFCS") that was mentioned earlier by Luke Guest in comments 
+on this AI.  If we are considering generalizing prefix notation for calls, 
+where F(A, B, C, D) is equivalent to A.F(B, C, D) in some circumstances, you 
+could argue that it should be equivalent in *all* circumstances.
+
+What the D language does (see 
+https://tour.dlang.org/tour/en/gems/uniform-function-call-syntax-ufcs) is when 
+it sees A.F(B, C, D) it first does a lookup of F in the scope associated with 
+A, and if that doesn't work, it treats it like a regular call on some visible 
+"F".  This approach becomes very handy when you are trying to chain together a 
+series of calls, as happens with certain kinds of pipelines or sequences of 
+operations that are being combined.  The other option is to use an explicit 
+operator like "&" but then you can get into a combinatorial explosion problem,
+because the type returned by one call need not be the same as is the 
+operand/result of the next call.  The other key point is that the operations 
+you want to call may be the result of instantiating a generic, and are 
+generally *not* primitives of the type of the first parameter, so requiring 
+that the operation be declared in the "scope" associated with the prefix 
+doesn't help.
+
+For what it's worth, ParaSail follows this UFCS principle, though I didn't 
+know it had a name... ;-)  And ParaSail actually always looks in the scope 
+associated with the types of all of the parameters (and the result, for that 
+matter), so pulling one parameter out front actually doesn't change the 
+lookup process at all.
+
+In any case, more food for thought on this AI.  Probably need to assign it to 
+someone if we want it to progress further.  But I guess we might want to talk 
+about it in an ARG meeting first, to establish intent.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Wednesday, March 11, 2020  8:46 PM
+
+> What the D language does (see
+> https://tour.dlang.org/tour/en/gems/uniform-function-call-synt
+> ax-ufcs) is when it sees A.F(B, C, D) it first does a lookup of F in 
+> the scope associated with A, and if that doesn't work, it treats it 
+> like a regular call on some visible "F".
+
+Could you explain how this differs from what is currently proposed? "Some
+ visible F" doesn't make much sense, since that would seem to ignore the first 
+parameter altogether.
+
+Perhaps you mean "some visible F with a first parameter of type A"? (That 
+would effectively drop the "primitiveness" requirement on the function, and 
+require looking through the universe for a matching function. Of course, 
+"looking through the universe" is something that compilers are prepared to do 
+anyway, so it might not be terrible.)
+
+However, that would be rather incompatible with the existing rules (much more 
+so than any of the previous proposals). There are a lot of routines in the 
+universe, and obsolete routines with matching names are not uncommon 
+(certainly not in my code).
+
+For example, Claw has a bunch of classwide routines like Move and Resize that 
+were (functionally) replaced in later versions with primitive versions (so 
+they could be overridden). The original versions (in a separate package) just 
+make a dispatching call to the primitive versions now; we kept them to avoid 
+breaking all existing code (we did mark them as obsolete).
+
+That package has lots of commonly used routines, so it is very commonly 
+withed. The net effect would be that every prefix call to Move or Resize would 
+be ambiguous, and qualification could not fix that (since the routines have 
+identical profiles). That would mean that one would end up having to fall back 
+on use clauses or lengthy expanded names for almost any call to these commonly 
+used routines. (I note that package use clauses probably don't work on this 
+pair of routines either, because the profiles are functionally identical.)
+
+I really dislike the idea of have reasonable scenarios where the use of prefix 
+notation is impossible for an ADT (my preference is either to write all prefix 
+calls or all normal calls -- switching back and forth is just confusing to 
+readers). And even worse is scenarios where prefix calls are legal in Ada 2012 
+that cannot be made legal in Ada 202x without a significant rewrite. So I 
+suspect that this would have been a good idea, but it is too late for Ada -- 
+it will have to wait for an Ada successor language.
+
+****************************************************************
+
+From: Richard Wai
+Sent: Wednesday, March 11, 2020  9:07 PM
+
+I honestly cannot see the value of the trouble. Prefixed calls are just 
+syntactic sugar. Prefixed calls for tagged types is about invoking an 
+operation ("method") on an object in the OOP sense, so it seems natural in 
+that context. But calling a subprogram with some parameters is simply a 
+different paradigm. 
+
+In short, something like My_String.Put_Line is a evil, and shouldn't be 
+encouraged.
+
+I'm with Randy on most of this, except for the Ada successor thing.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Wednesday, March 11, 2020  9:26 PM
+
+>> What the D language does (see
+>> https://tour.dlang.org/tour/en/gems/uniform-function-call-synt
+>> ax-ufcs) is when it sees A.F(B, C, D) it first does a lookup of F in 
+>> the scope associated with A, and if that doesn't work, it treats it 
+>> like a regular call on some visible "F".
+> 
+> Could you explain how this differs from what is currently proposed? 
+> "Some visible F" doesn't make much sense, since that would seem to 
+> ignore the first parameter altogether.
+
+I should have said "directly visible F."  That is, you fall back on normal 
+overload resolution and visibility if there is no "F" that is a primitive 
+or class-wide operation on A.  But you get the advantage of writing:
+
+A.F(...).G(...).H(...)
+
+Presuming F is a primitive of A or directly visible, G is a primitive of 
+the result of F, or directly visible, and H is a primitive of the result of G,
+or directly visible.
+
+In other languages these pipelines tend to be written as:
+
+    A.F(.....).
+       G(.....).
+       H(.....)
+ 
+> Perhaps you mean "some visible F with a first parameter of type A"? 
+> (That would effectively drop the "primitiveness" requirement on the 
+> function, and require looking through the universe for a matching 
+> function. Of course, "looking through the universe" is something that 
+> compilers are prepared to do anyway, so it might not be terrible.)
+
+No, F would have to be directly visible if it were not a primitive/class-wide 
+op of A.
+
+> ...
+
+Anyway, as I said, this was food for thought. ;-)
+
+****************************************************************
+
+From: Yannick Moy
+Sent: Thursday, March 12, 2020  3:50 AM
+
+I think your proposal makes a lot of sense. And your later clarification about 
+the rule ("primitive or directly visible operation") addresses the concerns of 
+Randy.
+
+>The other option is to use an explicit operator like "&" but then you can get 
+>into a combinatorial explosion problem, because the type returned by one call 
+>need not be the same as is the operand/result of the next call.  
+
+Can you explain the above sentence?
+
+>In any case, more food for thought on this AI.  Probably need to assign it to 
+>someone if we want it to progress further.  But I guess we might want to talk 
+>about it in an ARG meeting first, to establish intent.
+
+The alternative is to open an RFC on https://github.com/AdaCore/ada-spark-rfcs 
+for discussion.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, March 12, 2020  2:43 PM
+
+>> The other option is to use an explicit operator like "&" but then you can 
+>> get into a combinatorial explosion problem, because the type returned by 
+>> one call need not be the same as is the operand/result of the next call.  
+
+>Can you explain the above sentence?
+
+This was pretty specific to the kind of streaming framework they were trying 
+to create in this Ada-Europe paper, and I don't think I explained it very 
+well.  They indicated that if some of the elements of the stream were 
+collecting or reducing their input, then their output might be a different 
+type than their input, and you would need a bunch of different "&" operators
+if that is how you construct the pipeline.
+
+The paper should show up at Ada-Europe at some point.
+
+>> In any case, more food for thought on this AI.  Probably need to assign it 
+>> to someone if we want it to progress further.  But I guess we might want to
+>> talk about it in an ARG meeting first, to establish intent.
+
+> The alternative is to open an RFC on https://github.com/AdaCore/ada-spark-rfcs 
+> for discussion.
+
+For now I'll leave the discussion here on Ada-Comment, since it is very much 
+still in the brainstorming phase.
+
+****************************************************************
+
+From: Emmanuel Briot
+Sent: Thursday, March 12, 2020  2:46 AM
+
+I am not sure how much I would use this extended notation myself, but the
+UFCS page for D shows an interesting example, which roughly translated
+to Ada would be:
+
+    A : constant Int_Array := (….)
+    B : constant Int_Array := Evens (Divide (Multiply (A, 10), 3));
+
+which is kind of hard to read: you have to read from the inside out to understand
+the order of operations, and the argument for divide is quite far away. Instead,
+Tuck’s proposal would allow us to write something like:
+
+    A : constant Int_Array := (….)
+    B : constant Int_Array := A.Multiply (10).Divide (3).Events;
+
+which fixes both readability issues, something Ada designers are typically
+fond of, and rightly so.
+
+
+
+Another compelling argument, already mentioned by Tuck, is the case of
+generic instances. Those can never be primitive operations, so can never be
+called with the prefix notation
+
+    type C is tagged private;
+    function Op is new Some_Generic (C);
+
+    C.Op;   —  not possible in Ada12, not a primitive operation
+
+In this case, Op is defined in the same scope as C, so if we do not like Tuck’s
+full idea, we could also restrict the search for matching subprograms to those
+declared in the same scope as the type, even if they are not primitive operations.
+That would also work for the first example above, assuming Divide, Multiply and
+Events are declared in the same scope as Int_Array, which in practice is often
+the case.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, March 12, 2020  4:23 PM
+
+> In this case, Op is defined in the same scope as C, so if we do not 
+> like Tuck’s full idea, we could also restrict the search for matching 
+> subprograms to those declared in the same scope as the type, even if they 
+> are not primitive operations.
+> That would also work for the first example above, assuming Divide, 
+> Multiply and Events are declared in the same scope as Int_Array, which 
+> in practice is often the case.
+
+For what it is worth, in the Ada-Europe paper, they were using generics that 
+were unlikely to be instantiated "inside" the right scope, so this wouldn't 
+solve their problem.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, March 12, 2020  5:13 PM
+
+... 
+> Another compelling argument, already mentioned by Tuck, is the case of 
+> generic instances. Those can never be primitive operations, so can 
+> never be called with the prefix notation
+> 
+>     type C is tagged private;
+>     function Op is new Some_Generic (C);
+> 
+>     C.Op;   -  not possible in Ada12, not a primitive operation
+
+???
+
+Assuming this is in a package spec and that Op has a parameter of type C, and 
+that an object of C is used as the prefix ;-), Op *is* a primitive operation, 
+and this is legal in Ada 2012. 3.2.3(6) says "any subprograms ...
+declared immediately in the same package specification". It doesn't say 
+anything about the form of the declaration (renamings are also primitive -- 
+that's a bug IMHO but it is what it is).
+
+I'd guess you meant something more like:
+
+    generic
+       type Priv is private;
+    package Gen is
+       function Op (A : Priv) return Natural;
+    end Gen;
+
+    type C is tagged private;
+    package Inst is new Gen (C);
+    Obj : C;
+
+    N : Natural := Obj.Op;  -- not possible in Ada12, not a primitive operation
+
+But this remains illegal in Tucker's proposal unless a use clause is given on 
+Inst.
+
+Which of course brings up a quibble here: the need to have use clauses to 
+allow this notation means that this extension is not of much value to the 
+use-adverse (as those people never have much of anything directly visible).
+Not sure how useful such a notation would be (while the tagged prefix 
+notation eliminates both the need for use clauses AND the need to figure out 
+exactly where the operation you need is declared -- a substantial reason that
+it was accepted in the first place). ("use all" makes the same operations 
+visible that are directly available in prefix notation anyway, so it doesn't 
+help.)
+
+****************************************************************
+
+From: Jean-Pierre Rosen
+Sent: Friday, March 13, 2020  1:13 AM
+
+> But you get the advantage of writing:
+> 
+> A.F(...).G(...).H(...)
+> 
+> Presuming F is a primitive of A or directly visible, G is a primitive 
+> of the result of F, or directly visible, and H is a primitive of the 
+> result of G, or directly visible.
+> 
+> In other languages these pipelines tend to be written as:
+> 
+>     A.F(.....).
+>        G(.....).
+>        H(.....)
+
+Which I feel is much more readable. I usually first concentrate on WHAT is
+returned before caring about HOW it is obtained. So starting with the 
+outermost call makes sense. Oh well, readability is a matter of taste, and how 
+you've been educated in the first place...
+
+> I am not sure how much I would use this extended notation myself, but 
+> the UFCS page for D shows an interesting example, which roughly 
+> translated to Ada would be:
+> 
+>     A : constant Int_Array := (....)
+>     B : constant Int_Array := Evens (Divide (Multiply (A, 10), 3));
+
+No kidding! Any sane Ada programmer would redefine operators and write:
+
+>     A : constant Int_Array := (....)
+>     B : constant Int_Array := Evens ((A * 10) / 3);
+
+Which is even better than UFCS. But maybe D is lacking operator redefinition? 
+(I didn't check)
+
+****************************************************************
+
+From: Yannick Moy
+Sent: Thursday, March 12, 2020  3:50 AM
+
+****************************************************************

Questions? Ask the ACAA Technical Agent