Overload resolution simplification
forax at univ-mlv.fr
Tue Aug 13 14:07:54 PDT 2013
On 08/11/2013 11:50 AM, maurizio cimadamore wrote:
> On 10-Aug-13 10:58 PM, Remi Forax wrote:
>> It's not less magic. It's no magic.
> This is a bit unfair. I think there's still plenty of magic going on.
> Note that the path that led us there is that we were uncomfortable in
> having the compiler to pick one method over another because of some
> weird error in some implicit lambdas. Now - if you have something like:
> m(Function<String, Integer> f)
> m(IntFunction<String> f)
> And a lambda like
> it could be trivial to see that the second method is the one you want.
> On the other hand, consider the following overload of _non generic_
> m(Function<String, Integer> f)
> m(IntFunction<Integer> f)
> If we start accepting this (overload between non-generic methods - so
> everything is ok, no?) - then we open up can of worms in which an
> error in one speculative type-check will cause the method not to be
> applicable - i.e. :
> m(x->x.length()) //no member length() in Integer, so the first method
> is selected
> m(x->x.intValue()) //no member intValue() in String, so the second
> method is selected
> This was what the compiler was doing - and EG expressed some concerns
> with this - and for a good reason I think. A seemingly innocuous
> refactoring of a lambda body can trigger a change in overload
> resolution of the enclosing method call.
> In other words, even when there's an overload between non-generic
> methods, it gets very muddy very soon as soon as you consider all
> consequences of 'adding more magic'. Suddenly you have to start
> thinking about errors in the lambda body to reason about method
> applicability, and that's brittle (in fact, even though an earlier
> version of flatMap was able to disambiguate because of this, we
> decided NOT to rely on this as too magic/brittle).
> So, the only meaningful question here is - where should we draw the
> line? I see three options:
> 1) Allow non-generic overloads, provided each overload forces same
> choice on lambda parameter types
> 2) Expand on 1a, implementing a more complex logic that would also
> work on generic methods
> 3) Disallow implicit lambdas when overloads of the same arity are
> I think 2 was considered also too complex, as the analysis would have
> to treat inference variables that depend on the return type in a
> different way (i.e. Comparator.comparing won't work) - and that's
> surprising. So that's mostly a dead end.
> The choice is between 1 and 3. As a result of the current
> simplification, we are in 3. Now, I'm not totally against 1 - I think
> it's a legitimate position, but there are things about that approach
> that worry me:
> *) inconsistencies between non-generic vs. generic methods
> *) hard to parse when parameter types are ordered in different ways
> with different SAM types (see my earlier example on this list)
> *) will miss ambiguities when passing a lambda to _semantically_
> unrelated overloads - i.e.
> m(Function<String, Integer>)
> This example passes the rule that all overloads force same choice on
> lambda parameters. Should we accept a method call like the following?
> Well, if we follow that scheme, the answer is yes - and the candidate
> is chosen depending on the return type of foo(String). I think that
> this choice, while legitimate, is already 'too magic' - we have to
> very different targets there - with entirely different semantics; I
> think in such cases I would be more comfortable in having a more
> explicit form of disambiguation at the call site.
Well Ia gree,
just nitpicking about the semantics, in a perfect world all overloads
are semantically equivalent, otherwise you shall not use overloads.
> Last point - it looks to me that all cases in which we would prefer 1
> over 3 have to do with primitive specialization of SAM. On the one
> hand, the Java type-system features this split between primitive and
> reference types - so that's what we get; on the other hand, if (big if
> :-)) we had some form of primitive vs. reference unification, how many
> cases would be left that 1 could support that 3 cannot? Are we
> comfortable in _permanently_ adding more complexity to an already very
> complex area of the language, that could then be useless (at best, or
> even fire back) if/when the type-system is made more uniform?
More information about the lambda-spec-observers