Lambda and method reference inference

Brian Goetz brian.goetz at oracle.com
Thu Dec 6 14:24:44 PST 2012


In the assignment case, there's usually not a problem.  But nearly all 
the pain (which we're still not done with, by the way) has been where 
type inference and method overload selection interact.  You've seen the 
"cyclic inference" errors in plenty of cases where it "should" be 
"obvious" what the right answer is.

By giving more latitude to the compiler to consider box and unbox 
conversions, you deprive the compiler of type information with which to 
make better inference decisions.  Consider the following simple example:

Given overloads:
   m(IntIntFn)
   m(Fn<Integer,Integer>)

and a call

   m((int x, int y) -> ..., null)

Under the current rules, with the explicit types, we can discard the 
second candidate early.  The selection of the target type IntIntFn may 
further give us type information which can be used elsewhere in 
inference.  If we have to allow for the idea that we might be calling 
either version of m, not only do we have less information, but we also 
are more likely to get into the stability problems that the EG quite 
broadly agreed we wanted to stay away from (where small changes here can 
silently change overload choices.)

By picking the assignment case, you've ignored 99% of the complexity. 
All the complexity is in the interaction of inference and overload 
resolution.  If this restriction gives us even 1% better results here -- 
and it does -- it's totally worth it in my book.

On 12/6/2012 5:01 PM, Remi Forax wrote:
> On 12/06/2012 10:03 PM, Brian Goetz wrote:
>> We did care -- a lot -- but in the other direction :(
>>
>> This is one of those things that seems obvious at first but in reality
>> has a poor cost-benefit balance.
>>
>> First of all, in your example, you can get what you want with:
>>
>>       A<Integer> c = (x, y) -> x + y;
>
> it's not what I want, you can not do the same trick with:
>    A<Integer> c = (int x, int y) -> x == y;
> or
>    A<Integer> c = (int x, int y) -> { list.add(x, list.remove(y)); }
> or any calls that makes a difference between int and Integer.
>
>>
>> If you take the trouble to provide explicit parameter types, you are
>> saying "I want these exact types."  I can understand why you would
>> want the compiler to "just do what I mean", but allowing this degree
>> of flexibility unfortunately has a cost, and one of those costs is
>> more inference failures in other cases.
>
> can you be a little more specific, which failures ?
>
>>
>> At the same time, the benefit is low -- to fix the problem, either
>> leave out the types and let the compiler infer them, or use the exact
>> types you need (there's a good chance your IDE will provide them for
>> you anyway.)  It's an easy and local fix.
>
> The compiler infers the wrong type and I can't using the right type that
> exactly my problem.
> The best I can do is introduce a temporary method which is really non
> intuitive
>
> void foo(int x, int y) { list.add(x, list.remove(y)); }
>
> A<Integer> c = (int x, int y) -> MyClass::foo;
>
> and sorry, I don't believe that the IDE will cope with that (at least
> not before 2 years).
> This 'feature' make even the job of IDE writer far more complex, by
> example refactoring a lambda to a method reference and vice-versa will
> have to deal with stupid discrepancies like this one.
>
>>
>> We do allow this flexibility for method references because the user
>> has no control over the types of the referred-to method the way he
>> does with lambda parameter types.  And method references exhibit more
>> inference failures.
>>
>> The semantics are modeled on the following intuition:
>>  - typing for lambda expressions is like overriding a method;
>>  - typing for method references is like calling a method.
>
> I understand why when inferring a lambda with no specified type for
> formal parameter should be restricted to overriding rules but I don't
> understand why lambda with specified types for formal parameters has to
> have different rules different than the one for method reference.
>
> cheers,
> Rémi
>
>>
>>
>> On 12/6/2012 1:56 PM, Remi Forax wrote:
>>> I think i've already raised this point in August during the face to face
>>> meeting and at that time nobody care, maybe this time, I will have more
>>> chance :)
>>>
>>> with
>>>      interface A<T> {
>>>        T foo(T a, T a2);
>>>      }
>>>
>>> this compile:
>>>      A<Integer> b = Integer::plus;    // with plus defined as int
>>> plus(int,int)
>>>
>>> but not this one:
>>>      A<Integer> c = (int x, int y) -> x + y;
>>>
>>>
>>> Can we, please, have the same rules for both method references and
>>> lambdas that have their type of parameters specified ?
>>>
>>> cheers,
>>> Rémi
>>>
>


More information about the lambda-spec-observers mailing list