Inference error

Dan Smith daniel.smith at oracle.com
Wed Dec 12 12:29:03 PST 2012


On Dec 12, 2012, at 11:05 AM, Sam Pullara <spullara at gmail.com> wrote:

> The code itself might be a bad idea, just experimenting, but:
> 
> https://github.com/spullara/java-future-jdk8/commit/2fa4321a450ca56e3a0958214c7cdcd9bca1ad84
> 
> The third test fails to compile with this error:
> 
> java: no suitable method found for curry(map::put,java.lang.String)
>    method spullara.util.Currier.<T,U,V>curry(spullara.util.Currier.C2<T,U,V>,U) is not applicable
>      (Cannot instantiate inference variables U,V because of an inference loop)

Here's what inference in this case looks like.

interface C2<T,U,V> { T invoke(U u, V v); }
interface C1<T,U> { T invoke(U u); }

<T, U, V> C1<T, V> curry(C2<T, U, V> get, U u);

Map<String, Integer> map = new HashMap<>();
C1<Integer, Integer> c1 = curry(map::put, "test");

For applicability testing, there are two inference constraints:

map::put -> C2<alpha, beta, gamma>
"test" -> beta

---

The currently-implemented strategy infers

beta :> String

and then resolves

beta = String

But we know nothing about gamma at this point, and the first constraint can't be resolved until we have concrete parameter types (instantiations for beta and gamma) to work with.  So inference is stuck, and an error occurs.

---

What we plan to implement (this is alluded to in the 0.6.0 spec, but not explained in the inference section yet; I'm going to send a high-level summary soon) is  this:

"test" -> beta

reduces to

beta :> String

while 

map::put -> C2<alpha, beta, gamma>

is "stuck" and set aside for later.  This 'curry' method is considered "provisionally applicable".

_After_ overload resolution is done, we can come back and look at a new constraint:

C1<alpha,gamma> <: C1<Integer,Integer>

giving us

alpha = Integer
gamma = Integer

And then we can resolve beta=String and gamma=Integer, updating the 'map::put' constraint to

map::put -> C2<alpha, String, Integer>

Which works out.

---

Except that your example makes interesting use of overloading:

<T, U> C0<T> curry(C1<T, U> get, U u); // don't want this
<T, U, V> C1<T, V> curry(C2<T, U, V> get, U u); // want this

With a naive approach, both of these would be provisionally applicable, since the method reference constraint would be "stuck" in both cases, and so there would be an ambiguity.  We're looking at three different ideas that would avoid that problem:

1) Enhance "potentially applicable" for method references, so that the first candidate is dismissed because there's no unary 'put' method.

2) Since there is only one possible interpretation of 'map::put', handle that method reference bottom-up rather than the usual top-down approach.

3) Eagerly resolve U before overload resolution, since it doesn't show up in the return type.  This would "unstuck" the constraint targeting C1 in the first candidate, which would be enough to demonstrate that the first candidate is inapplicable.

Some or all of these may make the cut.  We've been focusing our efforts on lambda expressions, not method references, as we get the framework in place, and then these things can be tweaked.

—Dan


More information about the lambda-spec-experts mailing list