Why can't Kotlin infer types here?

class A<K, L>
class B<K, L> {
  fun <M> map(f: (L) -> M): B<K, M> = TODO()
}
class C<N>

fun <P, Q> buildB(builder: B<P, P>.() -> B<P, Q>): B<P, Q> = TODO()

fun <R, S, T> A<R, S>.connect(b: B<in S, T>): C<T> = TODO()

val foo: C<Int> = A<Int, String>().connect(buildB { map { it.length } })
//                                 ^^^^^^^ - cannot infer T
//                                         ^^^^^^ - cannot infer Q

Is it because the type checker has to go “too deep” into the AST? On the surface of it, it could figure out that T = Q (from equating types of parameter b in connect with return type of buildB: B<in S, T> = B<P, Q>), similarly, Q = M, and finally M = Int.

A more immediate question: what would be the minimal change that would introduce minimal amount of explicit types to make it compile?

“it works on my machine ¯\(ツ)/¯”
What kotlin-version are you using?

Wow. I’ve tried 1.2.71 and 1.3 on the kotlinlang site, both failed.

Upgraded Kotlin plugin to 1.30, and everything is fine now. Strangely, in play.kotlinlang.org everything is fine as well. I must have done something stupid, maybe a typo or something…

OK, turns out it doesn’t work if we remove the hint on val foo, but instead try to use the result in the context of some known type:

class A<K, L>
class B<K, L> {
  fun <M> map(f: (L) -> M): B<K, M> = TODO()
}
class C<N> {
  fun bar(v: N): Unit = TODO()
}

fun <P, Q> buildB(builder: B<P, P>.() -> B<P, Q>): B<P, Q> = TODO()

fun <R, S, T> A<R, S>.connect(b: B<in S, T>): C<T> = TODO()

val foo = A<Int, String>().connect(buildB { map { it.length } }).bar(5)

(In fact, I was playing with this version, and then trimmed it down a bit to get a more minimal example, since it was still not compiling in 1.2.71)

use this and it works.
(I assume you need a version newer than 1-3-0-rc-190, but didn’t try)

1 Like

Thanks, it works indeed! I’ve tried 1.3.0 release version as available on the Github.