heads-up: biggie overload rewrite

Dan Smith daniel.smith at oracle.com
Thu Jul 25 12:49:56 PDT 2013


It's a question of what the API should look like.  To overload methods like this is to make a calculated choice that only non-overloaded method references and explicitly-typed lambdas can be used as arguments.  I'm not sure if that choice has actually been made in this case, or if the library folks were waiting for the dust to settle before deciding how to handle 'comparing'.

From a language perspective, the problem is simple: we can't type check 's.length()' until we know that 's' is a String, and we can't figure that out until we look at the assignment target, and we can't do that until overload resolution is done.  In other words, there is not a context-independent way to figure out that 'Comparator.comparing(s->s.length())' is best treated as an invocation of  'comparing(ToIntFunction)'.

—Dan

On Jul 25, 2013, at 1:40 PM, Sam Pullara <spullara at gmail.com> wrote:

> Bummer. That is going to be surprising for people, especially with comparators.
> 
> Sam
> 
> On Jul 25, 2013, at 12:37 PM, Maurizio Cimadamore <maurizio.cimadamore at oracle.com> wrote:
> 
>> On 25/07/13 20:25, Maurizio Cimadamore wrote:
>>> On 25/07/13 20:22, Maurizio Cimadamore wrote:
>>>> On 25/07/13 19:15, Sam Pullara wrote:
>>>>> I just did a build this morning and still see these fail:
>>>>> 
>>>>>          Comparator<String> comparator = Comparator.comparing(String::length);
>>>>>          Comparator<String> comparator = Comparator.comparing(s -> s.length());
>>>>> 
>>>>> Are they still supposed to result in:
>>>>> 
>>>>> java: reference to comparing is ambiguous
>>>>>    both method <T>comparing(java.util.function.ToLongFunction<? super T>) in java.util.Comparator and method <T>comparing(java.util.function.ToDoubleFunction<? super T>) in java.util.Comparator match
>>>> Possible that you didn't pick up the changes?
>>> The first work for me (haven't tried the second but should be ok).
>> Sorry - was doing the wrong experiment; the first one WILL work - but not the second; the problem is that the signature:
>> 
>> public static <T> Comparator<T> comparing(ToIntFunction<? super T> keyExtractor) {
>> 
>> doesn't play by the rules illustrated in my email (T is included in the return type).
>> 
>> Maurizio
>>> 
>>> Maurizio
>>>> Maurizio
>>>>> Sam
>>>>> 
>>>>> On Jul 25, 2013, at 9:47 AM, Maurizio Cimadamore <maurizio.cimadamore at oracle.com> wrote:
>>>>> 
>>>>>> Dear lambdians,
>>>>>> I've just pushed a patch [1] that enhances javac overload
>>>>>> resolution/most specific story in several ways. One of the most notable
>>>>>> effects is the removal of the dreaded 'inference loop' message [2]. The
>>>>>> logic behind that message was noble: at the time we thought it would
>>>>>> have been better to report an error when the compiler was forced to
>>>>>> infer a variable to some 'default' instantiation (such as j.l.Object),
>>>>>> as this could cause severe downstream problems when type-checking a
>>>>>> lambda whose body depended on that choice. However, this was before we
>>>>>> added the more complex inference support; now that we have a more
>>>>>> capable inference engine, with all bells and whistles, we also have a
>>>>>> bigger degree of complexity and, because inference constraints are
>>>>>> propagated transitively, the line between default and non-default
>>>>>> instantiation has become a lot fuzzier than it used to be. Hence the
>>>>>> decision of getting rid of that logic (and related error message) -
>>>>>> which also makes the language more consistent (as inference typically
>>>>>> only gave such errors when lambdas/method references are present).
>>>>>> 
>>>>>> Other improvements in sight for the structural most specific logic; many
>>>>>> of you [3,4,5] have reported cases in which the compiler was unable to
>>>>>> distinguish between several signatures, where the 'right' choice seemed
>>>>>> indeed really easy. The problem in that case was that the structural
>>>>>> most specific check would only kick in if the compiler can prove that
>>>>>> all target types agree on the parameter types to be inferred for i.e. an
>>>>>> implicit lambda. In certain cases (when generic methods were used), the
>>>>>> compiler couldn't do that, so it basically went back to the old most
>>>>>> specific logic. Here's an example:
>>>>>> 
>>>>>> <T,R> Stream<R> map(Stream<T> s, Function<T,R> f) { }
>>>>>> <T> IntStream map(Stream<T> s, ToIntFunction<T> f) { }
>>>>>> 
>>>>>> map(ss, s->s.length()); //now ok - used to be ambiguous
>>>>>> 
>>>>>> There are rules to this game though; if the variable to be inferred to
>>>>>> be able to type-check the lambda (T in the above example) happened to
>>>>>> depend on one of the inference variables mentioned in the method return
>>>>>> type, the most specific check would fail and the compiler would again
>>>>>> report an ambiguity. The reason for this is that it's not possible to
>>>>>> guarantee that the eager instantiation of T would remain the same after
>>>>>> looking at the target type (and we want overload selection to be
>>>>>> independent from the target type, as we believe it's crucial to keep the
>>>>>> model tractable for developers).
>>>>>> 
>>>>>> The last improvement is related to the way in which method arguments are
>>>>>> type-checked; javac is now able to reason about the subtle dependencies
>>>>>> that arise when a lambda is passed as an argument to a generic method;
>>>>>> in the above case for instance, javac will detect that there's a
>>>>>> dependency between T and R in the first method. In fact, if we had an
>>>>>> instantiation for T, we would then be able to type-check the lambda and
>>>>>> we will most likely be able to derive new constraints for R. So, it
>>>>>> would be mad for the compiler to go and try to infer R _before_ looking
>>>>>> at the lambda expression.
>>>>>> 
>>>>>> I think those improvements go a long way in terms of polishing the
>>>>>> overall overload resolution story that the language presents to
>>>>>> developers; it gets rid of several outstanding issues, and makes the
>>>>>> whole overload selection process more streamlined and consistent. I'm
>>>>>> looking forward to hear your feedback (and bug reports :-)) as you start
>>>>>> using the next promoted lambda bits.
>>>>>> 
>>>>>> Enjoy the ride!
>>>>>> 
>>>>>> [1] - http://hg.openjdk.java.net/lambda/lambda/langtools/rev/d34073d069c8
>>>>>> [2] -
>>>>>> http://mail.openjdk.java.net/pipermail/lambda-dev/2013-July/010352.html
>>>>>> [3] -
>>>>>> http://mail.openjdk.java.net/pipermail/lambda-dev/2013-July/010476.html
>>>>>> [4] -
>>>>>> http://mail.openjdk.java.net/pipermail/lambda-dev/2013-June/010088.html
>>>>>> [5] -
>>>>>> http://mail.openjdk.java.net/pipermail/lambda-dev/2013-July/010590.html
>>>>>> 
>>>>>> Maurizio
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> 
>>> 
>> 
> 
> 



More information about the lambda-dev mailing list