Fwd: Overload resolution simplification

Zhong Yu zhong.j.yu at gmail.com
Wed Aug 14 14:44:37 PDT 2013


On Sat, Aug 10, 2013 at 12:41 PM, maurizio cimadamore
<maurizio.cimadamore at oracle.com> wrote:
> On 10-Aug-13 5:50 PM, Zhong Yu wrote:
>>
>> I'd appreciate that very much! The map() overloading use case wouldn't
>> be rare in practice. People will feel bad if javac cannot see that the
>> lambda parameter type is obviously fixed.
>
> While I'm sympathetic with this position, this would mean that stuff like
>
> IntStream map(IntFunction<T>)
> <Z> Stream<Z> map(Function<T, Z>)
>
> map(x->1);
>
> would work, while the following 'static' variant:
>
> <U> IntStream map(Stream<U> s, IntFunction<U>)
> <U, V> Stream<V> map(Stream<U>, Function<U, V>)
>
> Stream<String> ss = ...
> map(ss, x->1);

Hi Maurizio, revisiting this issue, there are apparently two different
types of args for a method invocation:

1. non-lambda args; their types are known, independent of context.
2. lambda args; their types are to be inferred from context.

We shouldn't treat them as equals. Why don't we first use non-lambda
args to do some inference on the methods, and prune some overload
candidates, before moving on to resolve the lambda args. This will
solve the above problem. It'll also solve the problem of

    Comparator<String> comparator = Comparator.comparing(s->s.length());

where, in the first step, we don't look at the lambda arg; from the
traditional inference rules, we get T=String. In the second step, we
resolve the meaning of the lambda through these method signatures:

    <U> comparing( String->U )
    comparing( String->int )
   etc

and conclude that `s` is a `String`, therefore `s->s.length()` is a
`String->int`.


>
> Would _not_ work. And it feels a bit weird - if the first case is deemed
> important, the second is equally important - it's just same example in a
> different style.
>
> Then I guess next step would be to say - obviously javac can see what 'U' is
> by looking at the input stream. Which is essentially where we were: in this
> case instantiating U is fine but there are cases (Comparators.comparing)
> where it's not ok. And it seems like not everybody is comfortable in making
> those subtle distinctions.
>
> It's obviously a problem of 'where to draw the line'. But there are
> advantages in scaling back a bit and give up a bit of expressiveness as the
> model you get is much simpler to explain and so are its boundaries. This
> property is not to be underestimated.
>
> Note that the 'same parameter' rule you are advocating for requires global
> reasoning: to see whether a lambda can be type-checked you have to consider
> all matching signatures and see if they impose same parameter on the
> implicit lambda. This can be trivial to see in simple examples (such as map)
> - but what if the example is more convoluted? I.e. many overloads? Or
> different sam types using different ordering for type parameters i.e.
>
> Function1<X, Y> {
>    Y apply(X x)
> }
>
>
> Function2<X, Y> {
>    X apply(Y x)
> }
>
> I believe that in such cases (or even more complex ones with nested SAM
> types as type-parameters) we'll be essentially at compiler's mercy - i.e. it
> would be very hard to judge whether a method call would succeed w/o trying
> it first on javac. And that's bad too.
>
> Maurizio


More information about the lambda-spec-observers mailing list