Home > Back-end >  Interfering type parameter based on another type parameter
Interfering type parameter based on another type parameter

Time:01-26

Is it possible to Kotlin Compiler to infer type parameter based on another type parameter? That is: infere type A based on type B in IntExample class definition.

interface Something<A>

interface Example<A, B: Something<A>>

class IntSomething: Something<Int>

// Can Int type parameter be infered by the fact that IntSomething is Something<Int>?
// If yes, how could I specify B type parameter without specifing the infered A (Int) parameter?
class IntExample: Example<Int, IntSomething> // specifing Int here is painful!!!

Imagine that we have more type parameters like that - it will be a lot of boilerplate to specify each of them if some might (theoretically) be infered.

EDIT

After an exhausive respose from @KarstenGabriel I will extend the previous example, to make it clear what for are the type parameters used here:

interface Something<A> {
   val sth: A
}

interface Example<A, B: Something<A>> {
   val eg: A
   val something: B  
}

data class IntSomething(override val sth: Int): Something<Int>

data class DescribedIntSomething(
    override val sth: Int, 
    val description: String
): Something<Int>

data class DescribedIntExample(
   override val eg: Int,
   override val something: DescribedIntSomething,
): Example<Int, DescribedIntSomething> // specifing Int here is painful

fun main() {
  val describedIntExample = DescribedIntExample(
    eg = 1,
    something = DescribedIntSomething(1, "Just one")
  )
  
  // We have to know that something is DescribedIntSomething to read description.
  // Neither `*` nor `Something<Int>` is sufficient, we need `B: Something<Int>` to keep B
  val description = describedIntExample.something.description 
  println(description)
}

So we use the type parameters - A and B as return values from eg and something

  1. Wildcard * cannot be used as it is just means Any?. We need to keep concrete type B. (eg. to enable reading descrption in the example)
  2. Wildcard Something<Int> cannot be used instead of B: Something<Int> for thesame reason as in the point 1

Maybe it's just an language design level. The type parameters do exists in compile time (they are ereased later), so theoretically Example<DescribedIntSomething> might be sufficient instead of Example<Int, DescribedIntSomething> as DescribedIntSomething is Something<Int>

CodePudding user response:

The problem is solvable under some assumptions:

  1. You need type parameter A only for property eg (or other similar cases).
  2. It is okay that we convert eg to be a function instead of a property.
  3. The desired value for eg (your A value) can be derived from the value of something (your B value). If the value is not inferred, then it is not possible to infer the type.

We then define Example without parameter A:

interface Example<B: Something<*>> {
    val something: B
}

Now inside Example we do not have a possibility to obtain the type parameter of B's Something-type, because the type parameter A on Something<A> does not exist at runtime. So you cannot just ask an instance of B what its Something-type parameter is. And Kotlin cannot do that either, because the information is just not there, if it is not explicitly specified by you.

But we know the parameter at compile-time which can be used in an inline extension function with reified parameter:

inline fun <reified A, reified B : Something<A>> Example<B>.eg(): A = something.sth

(documentation: Inline functions with reified parameter and Extension functions)

In this example I assume that eg should have the value of sth.

If you want eg to be private, you can put the function inside Example and make it private, otherwise it must be specified outside of the class.

Now you can just define your subclass without the need to specify the former A parameter, but you still have the right type:

data class DescribedIntExample(
    override val something: DescribedIntSomething,
) : Example<DescribedIntSomething>

fun main() {
    val describedIntExample = DescribedIntExample(
        something = DescribedIntSomething(1, "Just one")
    )
    val x = describedIntExample.eg() // x has inferred type Int and value 1
}
  •  Tags:  
  • Related