Overload resolution simplification

Dan Smith daniel.smith at oracle.com
Fri Aug 9 13:16:18 PDT 2013


If 'voidReturningMethod' is not overloaded, you're okay.  (That's my clause about "will ignore overloaded method references".)

Also, for Runnable vs. Callable, you're okay with a lambda expression, because 0-ary lambdas are always explicit (they have no parameter types to infer).

If we had a case of something like FileFilter vs. Consumer<File>, then an implicit lambda wouldn't always work.  Possible we'll special-case a void-/value-returning block body, considering that part of the "shape" or "arity".  But an expression lambda could be interpreted either way, depending on typing, so would be ambiguous.

—Dan

On Aug 9, 2013, at 12:55 PM, Sam Pullara <spullara at gmail.com> wrote:

> Is there anything we can do about:
> 
> ExecutorService.submit(object::voidReturningMethod)
> 
> being ambiguous?
> 
> Sam
> 
> On Aug 9, 2013, at 9:14 AM, Brian Goetz <brian.goetz at oracle.com> wrote:
> 
>> Note that in almost all the cases where we relied on fancy overload selection, we one-by-one accepted that it was better to mangle the names (flatMapToInt) anyway.  The number of remaining cases is small, and the sensible cases that fall in the space between what was allowed with the more complex scheme and what is prohibited under this scheme is surprisingly small.  Which suggests the return-on-complexity here for the more complex scheme is poor.
>> 
>> We were all a bit surprised that the new scheme works, but indeed it does seem to work pretty well.
>> 
>> In hindsight, this should not be surprising.  Type inference and overloading are ever in opposition; many languages with type inference from day 1 prohibit overloading (except maybe on arity.)  So for constructs like implicit lambdas, which require inference, it seems reasonable to give up something in overloading power to increase the range of cases where implicit lambdas can be used.
>> 
>> On 8/8/2013 8:19 PM, Dan Smith wrote:
>>> We spent some time at the EG meeting last week talking about the overload resolution story in the presence of lambdas/method references (and, why not, type argument inference).  There are a lot of tricky dependencies here, and the goal is to find a balance between expressivity and simplicity.
>>> 
>>> The sense I got from the meeting is that, despite our efforts to refine the story (there have been a few iterations), we're still not there yet in terms of simplicity.  In particular, I think what's crucial about the model I presented is that users can identify the difference between implicit lambdas that get type checked pre-overload-resolution and post-overload-resolution; the sanity check I got is that nobody will be able to make that distinction.
>>> 
>>> A couple of days later, Maurizio pointed out that, as we've iterated on our libraries, we've largely abandoned the space of programs that requires some of the more complex overload disambiguation machinery.  And looking more closely at those use cases, we agreed that we've probably been focusing too much on some atypical patterns.
>>> 
>>> So, let me propose a greatly simplified but probably not-very-noticeably less expressive approach:
>>> 
>>> Overload resolution will only check the arity of all implicit lambdas and will ignore overloaded method references.  If the body of a lambda is important for disambiguation, it must have explicit parameter types.
>>> 
>>> Benefits of this approach:
>>> - Very easy to understand -- it's mostly a syntactic distinction
>>> - Consistent between all different patterns of overloading that were previously treated differently
>>> - Facilitates a simple declaration-site warning check when method signatures conflict
>>> - Encourages use of explicit lambdas -- clearly acknowledges that we can't solve all inference problems with implicit lambdas
>>> - Avoids re-checking lambdas with different parameter types which means:
>>> -- Typing of lambda bodies is easier for users to process
>>> -- Implementations don't have to do speculative checking of arbitrary blocks of code
>>> -- Bad theoretical complexity goes away
>>> 
>>> We've thought about it for a few days and think this is a much better scenario for users and more in line with the EG's expectations (based on feedback both this year and last).
>>> 
>>> Any questions/concerns?
>>> 
>>> ---
>>> 
>>> Here's an example of something we would stop disambiguating:
>>> 
>>> interface I<T> {
>>>  <R> R map(Function<T,R> f);
>>>  int map(ToIntFunction<T> f);
>>>  long map(ToLongFunction<T> f);
>>>  double map(ToDoubleFunction<T> f);
>>> }
>>> 
>>> someIofString.map(s -> s.length());
>>> 
>>> Declaration-site workaround: rename the methods.
>>> 
>>> Use-site workaround: explicit parameter type:
>>> someIofString.map((String s) -> s.length());
>>> 
>>> ---
>>> 
>>> Here's an example of something else we would stop disambiguating:
>>> 
>>> static void m(Function<String,Integer> f);
>>> static void m(ToIntFunction<Object> f);
>>> 
>>> m(x -> x.length() > 10 ? 5 : 10);
>>> 
>>> ---
>>> 
>>> And here's something that we never could disambiguate in the first place (due to fundamental design constraints):
>>> 
>>> interface Comparators {
>>>  <T, R extends Comparable<? super R>> Comparator<T> comparing(Function<T, R> f);
>>>  <T> Comparator<T> comparing(ToIntFunction<T> f);
>>> }
>>> 
>>> Comparator<String> cs = Comparators.comparing(s -> -s.length());
>>> 
>>> ---
>>> 
>>> —Dan
>>> 
> 



More information about the lambda-spec-observers mailing list