[jsr-292-eg] Conversion of method reference to MethodHandle
Remi Forax
forax at univ-mlv.fr
Mon Jan 7 04:47:15 PST 2013
ping ...
On 12/31/2012 04:18 PM, Remi Forax wrote:
> On 12/31/2012 02:00 AM, Brian Goetz wrote:
>> To start with, the syntax that has been chosen for method references
>> is inadequate for method handles. This is because of the point you
>> raise -- there is no way to specify the type of the parameters, and
>> therefore no reasonable way (creating a customized functional
>> interface for each usage is not reasonable) to select between
>> multiple overloads of the same name. We have discussed explicit type
>> parameters, but they turned out (or so we still think, jury is still
>> out) that not adding this bit of syntax is adequate to what we are
>> doing here.
>>
>> Failing completely in the presence of overloading makes this a
>> crippled feature with even more sharp edges than it naturally has.
>
> I see that as a feature.
> People tend to use overloading where they should not. List::remove is
> a good example, JPanel#add is another one.
> The only case where it's correctly used is PrintStream#println,
> StringBuilder#append or Math::sqrt because all overloads have the same
> semantics.
>
> More fundamentally method reference does an early binding, so even a
> method reference with a functional interface is broken because the
> overload resolution should appear when calling it and not when
> creating it.
>
> The simple workaround is to create a bridge method, if you can't
> change the class to not use overloading.
> private static void myprintln(PrintStream stream, int value) {
> return stream.println(value);
> }
>
>> But that's not even the real objection,
>
> cool !
>
>> nor is this:
>>
>>> And because Brian will say that this is interesting but Oracle has
>>> no resource that can work on this,
>>> here is the patch for the compiler* part based on lambda/langtools :
>>> http://www-igm.univ-mlv.fr/~forax/tmp/method-ref-method-handle-langtools.diff
>>> and the patch for the jdk part based on lambda/jdk:
>>> http://www-igm.univ-mlv.fr/~forax/tmp/method-ref-method-handle-jdk.diff
>>
>> My true objection is deeper than any of these. After thinking about
>> it for a long time, I think this feature just doesn't belong. The
>> set of people who need method handle literals is a tiny sliver of the
>> Java developer base; complicating "everyone's Java" for the sake of a
>> few advanced users (100% of whom are qualified to generate bytecode)
>> is a bad stewardship choice.
>
> There are two points here:
> 1) the set of people interested by this feature is tiny.
> 2) it complicates Java for everybody
>
> Point 1.
> Perhaps, the set of people is actually not that big, it's the people
> that write language runtimes i.e. people that really need to use
> method handles.
> But there is a bigger set of people, the ones that will use that
> feature, i.e. all the people that actually use java.lang.reflect are
> people that will use that feature.
> The fact that there is no method reference literal make things like
> creating bean properties, interceptors, proxy etc, more painful that
> it should.
> So yes the actual set of people is not big, but the people that will
> use it is far bigger.
>
> Point 2.
> It's just not true, it's the beauty of target typing. The syntax
> already exists, the semantics is simple. The overhead is really tiny.
> You can compare it to the introduction of the hexadecimal syntax for
> double in Java 5.
> The hexadecimal syntax already exists, after Java 5, the syntax can
> used to specified double, not something really complicated.
>
>>
>> I'm one of those few who has to resort to writing ASM programs to use
>> MHs when I want them. So I know exactly how painful it is. And
>> still, I'd rather impose that pain on myself and my peers than
>> increase the complexity of Java for everyone for the benefit of a
>> small few.
>
> see point 1.
>
>>
>> The way I solved this for myself is I wrote a tool which
>> opportunistically mangles calls to Lookup.findXxx (with constant
>> arguments) into LDCs using bytecode mangling. From a
>> programming-model perspective, this is only slightly more painful
>> than what you propose -- and works more generally. (It also works
>> for indy.) I much prefer an approach like that.
>
> I've also written this kind of codes, John Rose did too and every
> people that have wanted to write JUnit Test using method handles have
> written something similar.
> But focusing only on actual users, people that are forced to use
> MethodHandle seems very wrong to me.
>
> Rémi
>
>>
>> On 12/30/2012 7:42 PM, Remi Forax wrote:
>>> [to lambda spec list and JSR292 EG list]
>>>
>>> I want to close one problem that we (the JSR 292 EG) have postponed to
>>> Java 8 when we were developing the JSR 292,
>>> a Java syntax to reference a method or a constructor as a method
>>> handle.
>>> This item was postponed for two reasons, the first one is that the JSR
>>> 292 was a JSR allowing to change the VM spec and not the Java spec and
>>> the second one was that at that time the syntax for method reference
>>> was
>>> in limbo and we didn't want to choose a syntax that may be incompatible
>>> with the upcoming lambda spec.
>>>
>>> Now that the syntax part of the lambda spec is frozen, we can solve
>>> that
>>> issue.
>>>
>>> Just before starting, I want to briefly explain why this syntax is
>>> needed.
>>> A runtime of a dynamic languages uses several well known function
>>> (usually specified as static method) implementing either part of the
>>> runtime logic like a function that check is the runtime class of an
>>> object is equals to a specified class or some global functions of the
>>> language it self (here is the file for JavaScript in Nashorn [1]).
>>> The main issue is that to be used in JSR 292 ecosystem, this functions
>>> must be handled by method handles and these method handles are defined
>>> are static final fields,
>>> because Java doesn't initialize these fields lazily, this greatly
>>> impacts the startup time of any dynamic runtime that runs on the JVM.
>>>
>>> So the goals of the syntax that convert a method reference to a method
>>> handle is:
>>> - to move some runtime checks at compile time because creating a
>>> method handle involve using a string and a signature.
>>> - to lazily create these method handle when needed (like the
>>> lambda).
>>>
>>> The proposed syntax is to use the same syntax as the method reference,
>>> so no new syntax.
>>> MethodHandle mh = String::length;
>>> the target type is a j.l.i.MethodHandle instead of being a functional
>>> interface
>>>
>>> For the semantics:
>>> Unlike the method reference with a functional interface, only
>>> unbunded method reference are allowed, so by example,
>>> a method reference starting with an expression or super are not
>>> valid.
>>> Moreover, because there is no type information that the compiler can
>>> extract from a MethodHandle, the semantics in case of several
>>> overloading methods, i.e. several applicable methods is simple, it's an
>>> ambiguity and is rejected by the compiler.
>>> More formally, a method reference can be converted to a
>>> j.l.i.MethodHandle,
>>> - only if it's form is
>>> Type::name, with Type an existing type and name a valid Java
>>> identifier or 'new'.
>>> - then from the type, the compiler gather all accessible methods
>>> declared in the type type or it's super type (class or interfaces),
>>> if a method is override-equivalent to another one, the one from
>>> the supertype is discarded.
>>> if the name is 'new', the compiler gather all constructors of
>>> the
>>> type type.
>>> if the type is an array, all public methods of j.l;Object are
>>> available plus clone()
>>> - if there are more than one method in the set of gathered method,
>>> the reference is ambiguous.
>>> - if there is no method, the reference is not available
>>> - if there is one method, a method handle will be created from
>>> this
>>> method.
>>>
>>> Here is some examples:
>>> class Test {
>>> public static void foo() { }
>>> }
>>> ...
>>> MethodHandle mh1 = Test::foo; // foo()V
>>> mh1.invokeExact();
>>>
>>> class Test
>>> public void bar() { }
>>> }
>>> ...
>>> MethodHandle mh2 = Test::bar; // bar(Test)V
>>> mh2.invokeExact(new Test());
>>>
>>> class Test {
>>> interface I {
>>> public void m();
>>> }
>>> static class A implements I {
>>> public void m() { }
>>> }
>>> }
>>> ...
>>> MethodHandle mh3 = I::m; // m(I)V
>>> mh3.invokeExact((I)new A());
>>>
>>> class Test {
>>> static class B {
>>> public B(int value) { }
>>> }
>>> }
>>> }
>>> ...
>>> MethodHandle mh4 = B::new; // B(int)
>>> B b = (B)mh4.invokeExact(3);
>>>
>>> class Test {
>>> class C { // inner class
>>> public C() { }
>>> }
>>> }
>>> ...
>>> MethodHandle mh5 = C::new; // C(Test)
>>> C c = (C)mh5.invokeExact(new Test());
>>>
>>> class Test {
>>> static class D {
>>> Object foo() { }
>>> }
>>> static class E {
>>> String foo() { } // covariant return type
>>> }
>>> }
>>> ...
>>> MethodHandle mh8 = E::foo; // foo(E)String
>>> String s3 = (String)mh8.invokeExact(new E());
>>>
>>> class Test {
>>> static class F {
>>> private static void m() { } // non visible method for the VM, ok
>>> in Java, so the compiler has to generate a bridge
>>> }
>>> }
>>> ...
>>> MethodHandle mh9 = F::m;
>>> mh9.invoke();
>>>
>>> MethodHandle mh10 = String[]::clone; // String[].clone returns
>>> Object, so it needs a bridge by the compiler
>>> String[] values = (String[])mh10.invoke(args);
>>>
>>> As you can see the syntax already exist, the semantics of the
>>> attribution (find the correct method) is simpler than the method call
>>> semantics (there is no inference) and after the attribution, the rules
>>> for the generation, bridge, etc. are the same as the one for the method
>>> reference with a functional interface as target.
>>>
>>> The compiler generates like the method reference an invokedynamic
>>> with a
>>> different bootstrap method that takes the constant method handle as
>>> parameter and create a constant callsite with this constant method
>>> handle (so at runtime a method reference seen as a method handle is
>>> always constant).
>>>
>>> And because Brian will say that this is interesting but Oracle has no
>>> resource that can work on this,
>>> here is the patch for the compiler* part based on lambda/langtools :
>>> http://www-igm.univ-mlv.fr/~forax/tmp/method-ref-method-handle-langtools.diff
>>>
>>>
>>> and the patch for the jdk part based on lambda/jdk:
>>> http://www-igm.univ-mlv.fr/~forax/tmp/method-ref-method-handle-jdk.diff
>>>
>>> cheers,
>>> Rémi
>>> * note, the mh9 doesn't work at runtime because it also doesn't work
>>> for
>>> method reference with a functional interface.
>>>
>>> [1]
>>> http://hg.openjdk.java.net/nashorn/jdk8/nashorn/file/b4b05457b8b2/src/jdk/nashorn/internal/runtime/GlobalFunctions.java
>>>
>>>
>>>
>
More information about the lambda-spec-observers
mailing list