> De: "daniel smith" <daniel.sm...@oracle.com>
> À: "amber-spec-experts" <amber-spec-experts@openjdk.java.net>
> Envoyé: Samedi 6 Octobre 2018 01:22:25
> Objet: Re: New JEP: Concise Method Bodies

>> On Oct 5, 2018, at 4:18 PM, Brian Goetz < [ mailto:brian.go...@oracle.com |
>> brian.go...@oracle.com ] > wrote:

>>> Can the expression before the :: refer to any method parameters? Of course, 
>>> it
>>> would still expect to pass those parameters into the method, so it's weird 
>>> to
>>> have the same parameter used in both ways, but does it make sense to forbid 
>>> it?

>> Yes, it makes sense to forbid. Here's why.

>> The framing of

>> method declaration = behavior literal

>> is meant to suggest that the thing on the RHS can be computed ONCE, and 
>> that's
>> the "body" of the method. Therefore, it should only be computable based on 
>> what
>> is known in the context of the method (static state for static methods,
>> instance state for instance method.) It is not merely a shorthand for "take 
>> the
>> method ref, turn it into a lambda, and then turn the body of that lambda 
>> into a
>> method body."

>> So the thing on the left of the :: must be something that could be, say, the 
>> RHS
>> of a field initializer of the appropriate static-ness.

>>> Now we want to introduce a second way to use method references, where that
>>> creation/evaluation distinction doesn't exist.
>> Same distinction. You can think of the creation as happening in <init> /
>> <clinit>.

>> Now, a fair question is: when should we evaluate that LHS? And, is it an 
>> error
>> or warning if that LHS is, for example, a nonfinal field? Good question! 
>> Thanks
>> for bringing this up.

> Yes, glad to see more discussion about this. We talked about it a bit weeks 
> ago,
> but never got past the "yeah, that's something to think about" stage.

> The challenge seems to be finding the right intuition.

> 1) One interpretation is that the method reference must be a 
> constant—something
> that can be translated into a JVM linkage instruction without executing any
> code. (Meanwhile, on the JVM side we've explored some features for declaring
> concrete methods without a Code attribute.)

> 2) Another interpretation is Brian's here: the evaluation of a method ref's
> receiver parameter occurs at class initialization, as if there were an
> initialization of a static field. This suggests that "linkage" of the method
> (i.e., computation of a function-like entity representing the body) occurs at
> class initialization.

> 3) Or, also suggested above: the evaluation occurs in a constructor, again as 
> if
> a (in this case, instance) field were initialized with the expression.

> 4) The evaluation occurs after the constructor has completed, making all
> fields—including those initialized in the constructor—available.

> 5) The evaluation occurs just before first method invocation.

> 6) There is no "linkage" step, the method reference is re-computed on every
> invocation. Method parameters are in scope. This is the "syntactic sugar"
> approach, and the one Kevin is taking issue with.

1 and 5 can be combined, it is more or less mindy aka method dynamic, you get 
the concrete method implementation the first time you call it like with 
invokedynamic the bytecode invokedynamic is not resolved until you nned to. In 
that case, you can see the implementation as a MethodHandle which is linked the 
first time the method is called. If it's a method handle, all combinators are 
available so you can accept any expression before the :: 

> I don't know the right answer, but here are some considerations:

> - Another problem with referring to method parameters is that users might 
> expect
> to exclude a referenced parameter from the parameters passed to the method 
> ref,
> but the behavior ends up being really ad hoc:

> static void m1(Foo x, Bar y) = x::m; // invoke x.m(x,y) or x.m(y)?
> static void m2(Foo x, Bar y) = y::m; // invoke y.m(x,y) or y.m(x)?
> static void m3(Foo x, Bar y) = f(x,y)::m; // invoke obj.m(x,y) or obj.m()?

The prototype implements 
x.m(x, y) 
y.m(x, y) 
obj.m(x, y) 

if :: is used to ease the delegation, it makes sense. 

> - It's not totally clear what we do with 'this'. Kind of seems like it should 
> be
> treated as the first parameter to be passed, but there are also examples in 
> the
> JEP that ignore it. And is it allowed to be referenced by the receiver
> expression?

> void reverse() = Collections::reverse; // invoke Collections.reverse(this)?
> int length(String s) = String::length; // invoke s.length()?
> void altMethod() = ThisClass::method; // invoke this.method()?
> void altMethod2() = this::method; // legal?

the first one doesn't work, you need -> Collections.reverse(this); 
the second works 
the third requires method to be static, so the VM invokes ThisClass.method() 
the last one is legal, by example you delegate between get() and orElseThrow() 
in a java.util.Optional. 

> - Delegation typically depends on an instance field provided as a constructor
> parameter, which won't work with (1)-(3), and if the delegation target is 
> meant
> to be mutable, won't work with (4)-(5) either.

> Foo target;
> int m1() = target::m1;
> int m2() = target::m2;

> - Ideally, we would not like to further complicate the class/instance
> initialization story by adding new execution timing, as in (4) or (5). What
> Brian's story ((2) and (3)) has going for it is that we already have the
> infrastructure to describe the timing of these evaluations.

> - We can simplify things considerably by prohibiting the expression form—must
> use Type::name. Effectively, (1) but without support for constant expressions.
> That means giving up on some use cases.

it means dropping most of the delegation use cases. 

Rémi 

Reply via email to