Overload resolution simplification

Zhong Yu zhong.j.yu at gmail.com
Thu Aug 15 16:26:33 PDT 2013


I had APIs that depended on last week's javac; they won't work after
the simplification. See my previous posts for my use cases.

Also I wish assignment context could be considered in the
    Comparator<String> cs = Comparators.comparing(s ->expr);
case (with overloaded comparing method). This use case seems rather
common (brought up by multiple users on lambda-dev list).



On Thu, Aug 15, 2013 at 6:10 PM, Maurizio Cimadamore
<maurizio.cimadamore at oracle.com> wrote:
> On 16/08/13 00:08, Zhong Yu wrote:
>>
>> On Thu, Aug 15, 2013 at 4:56 PM, Maurizio Cimadamore
>> <maurizio.cimadamore at oracle.com> wrote:
>>>
>>> On 15/08/13 22:36, Zhong Yu wrote:
>>>>
>>>> On Thu, Aug 15, 2013 at 9:00 AM, Maurizio Cimadamore
>>>> <maurizio.cimadamore at oracle.com> wrote:
>>>>>
>>>>> On 15/08/13 14:24, Zhong Yu wrote:
>>>>>>
>>>>>> That is not a reason for Java to reject it too. Java traditionally
>>>>>> does inference based on assignment context; it'll be nice to continue
>>>>>> to do that even with lambda arguments.
>>>>>>
>>>>> Well - that wasn't the point of my email; several posts in this thread
>>>>> were
>>>>> of the kind - 'why can't you guys just do what C# does' ?
>>>>> I only tried to show that there are similar limitations elsewhere.
>>>>>
>>>>> I explained at least 3-4 times as to why it is actually a good design
>>>>> choice
>>>>> not to allow stuff as Comparator.comparing overload - but somehow the
>>>>> message doesn't get across. It's not like we are making something
>>>>> dumber
>>>>> or
>>>>> different from what it used to be; it's true Java does inference based
>>>>> on
>>>>> target context - but that inference has _never_ affected the outcome of
>>>>> overload resolution - which helps overload selection to remain
>>>>> tractable
>>>>> (i.e. not NP-hard, to go along with Eric Lippert). We'd like to keep it
>>>>> that
>>>>> way. That means sticking with a design principle that guided us in the
>>>>> past
>>>>> - but I do understand that much of this stuff is subtle and hard to
>>>>> disgest.
>>>>>
>>>>> Maurizio
>>>>
>>>> I've been only arguing for the limited overload case where all
>>>> overloaded methods must agree upon the parameter types of implicit
>>>> lambda arguments. That is a tractable strategy. In your previous
>>>> example.
>>>>>
>>>>> m(x->g(y->f(...)))
>>>>> where m, g, f are overloads.
>>>>
>>>> if m,g,f satisfy the requirement about lambda parameter types, it is
>>>> as tractable as
>>>>     m( (X x)->g( (Y y)->f(...) ) )
>>>> If m,g,f don't satisfy the requirement, fail immediately.
>>>>
>>>> Your other objection is that lambda parameter types may depend on
>>>> method type variables; but it seems to me that we can first infer
>>>> method type variables from other arguments and assignment target,
>>>> after which the lambda parameter types are resolved per method,
>>>> therefore we can check whether all methods agree upon lambda parameter
>>>> types. This is done before selecting the most specific method.
>>>
>>> In the complex cases (such as Comparator.comparing) this is almost always
>>> the case; the lambda parameter type depends on some method type variable.
>>> Now, if the method type-variable is in a position that doesn't overlap
>>> with
>>> the return type, fine, we could do that (in fact that's how javac worked
>>> until last week).
>>>
>>> If there's an overlap between the return type and that method
>>> type-variable,
>>> then you have two choices:
>>>
>>> *) you throw the context into the overload machinery - thus making the
>>> problem much harder - i.e. in cases like
>>>
>>> m(g(x-> ...)) [see below]
>>>
>>> *) you throw the context or give up depending on what kind of context is
>>> -
>>> i.e. if assignment context then it's ok [see below]
>>>
>>> *) you give up (what javac did)
>>>
>>>
>>> So, to stress the point I'm making - in a case like this:
>>>
>>> SomeTarget s = g(x-> ...)
>>>
>>> you have only one target to consider when you do overload selection of g.
>>> In
>>> this case, however:
>>>
>>> m(g(x-> ...))
>>>
>>> You have multiple targets to throw into the overload selection for g -
>>> one
>>> for each m overload. This means overload resolution grows exponentially
>>> with
>>> the level of nesting.
>>
>> Yes, I'm not saying this example should work.
>
> Ok - so I believe you are saying you were ok with where javac was last week
> (before simplification), right?
>
>>
>>>
>>>
>>>> This process may be too complex for programmers to follow, which is
>>>> probably Dan's original point. I don't think people care. Nobody reads
>>>> the 40 page long JLS section 15.12, yet people are doing ok based on
>>>> intuition. I believe people's intuition can handle the limited
>>>> overload capability too; in all examples shown so far, none is
>>>> difficult to understand if the method is overloaded and the compiler
>>>> accepts the invocation. It is more difficult to understand why
>>>> overloading does not work in these cases.
>>>
>>> The problem is that people (not necessarily users) is gonna need to read
>>> that JLS section - as they will have to implement IDEs or different
>>> compilers. Do we want _all_ compilers in the world to sign up for that
>>> kind
>>> of combinatorial explosion? AFAIK none of the IDEs out there (but
>>> Netbeans,
>>> which uses javac itself as a backend) implements the full
>>> overload/inference
>>> check as described in the spec and as was implemented in javac - and you
>>
>> hey, even javac doesn't "implements the full overload/inference check
>> as described in the spec" :)
>
> Heh - not this week - but last week yes :-)
>
> Maurizio
>
>>
>>> know what? People didn't even realized that when using them.
>>>
>>> Maurizio
>>>>
>>>>
>>>> Zhong Yu
>>>
>>>
>


More information about the lambda-spec-observers mailing list