My generi extension function doesn't compile

Here is my code

class X<A> {
    val x = extensionX()
}
fun <A> X<A>.extensionX () = 1 //compiles

fun <N, B: Box<N, B>> B.extension() = this.a

abstract class Box<N, B:Box<N,B>>(val a: N) {
    abstract fun copy(new: N): B
    val boxed =  extension()  //doesn't compile
}

class MyBox<N>(a: N) : Box<N, MyBox<N>>(a) {
    fun value() = extension() //doesn't compile
    override fun copy(new: N) = MyBox(new)
}

val z = MyBox(5).extension() //compiles

(I added the copy method to demonstrate why I need this structure.)

I get two compilation errors when calling the extension

Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public fun <N, B : Box<TypeVariable(N), TypeVariable(B)>> TypeVariable(B).extension(): TypeVariable(N) defined in ...

I can’t see why the reciever type is mismatched, and how I can write this code differently

Generics hell. :confounded:

For the first case, I wonder if it’s because extension() requires a type B that extends Box, but the Box class doesn’t extend Box? I think this is usually fine in Java, but maybe Kotlin’s a bit fussier about this.

I would try explicitly specifying your types to see if you can get more info about what the actual problem is. IE, in your Box class, change it to val boxed = extension<N, Box<N, Box>>().

If you just need N, maybe this is good enough?

fun <N> Box<N, *>.extension() = this.a

Maybe there’s something wrong in the compiler. But this does works:

// fun <N, B: Box<N, B>> B.extension() = this.a
// Change B into Box<N, B>
fun <N, B: Box<N, B>> Box<N, B>.extension() = this.a

Let’s make some change to understand why your previous code does not work:

fun <EN, EB: Box<EN, EB>> Box<EN, EB>.extension() = this.a

abstract class Box<BN, BB: Box<BN, BB>>(val a: BN) {
    abstract fun copy(new: BN): BB
    val boxed = extension()  // let's call this Box::extension
}

class MyBox<MN>(a: MN): Box<MN, MyBox<MN>>(a) {
    fun value() = extension() // let's call this MyBox::extension
    override fun copy(new: MN) = MyBox(new)
}

For Box::extension, what we want is this@Box.extension(). We can find that we use Box<BN, BB> as the type argument of the type parameter EB.

fun <EN , EB /*replaced by Box<BN, BB>*/: Box<EN, EB>> EB.extension() = this.a

Soon we noticed EB needs to be the subtype of Box<EN, Box<BN, BB>> which is not possible.

2 Likes

Thank you. I’'m not sre I fully understand this, but your solution works fine