kosi-libs / Kaverit

A light multiplatform Kotlin reflection API
MIT License
17 stars 1 forks source link

Feature request: Type logic and variance #10

Open Jolanrensen opened 3 months ago

Jolanrensen commented 3 months ago

First of all, great job :) A multiplatform reflection library is a great addition to the ecosystem :)

We're looking at migrating a library to multiplatform in the future, but we rely heavily on reflection and type logic, so I'm curious to see how far Kaverit can help us.

I do have a couple suggestions and concerns.

Variance and * notations:

I noticed TypeToken.getGenericParameters() fills in * parameters of generics with Any no matter the variance or supertype:

generic<List<*>>().getGenericParameters()[0]

is given as kotlin.Any. In this case, * means Any?, but I see you don't support nullability on purpose, so that's fine.

However, for

interface Test<T : Number>
generic<Test<*>>().getGenericParameters()[0]

it returns kotlin.Any too. I'd expect there to be a way to know that * means Number here.

Finally,

generic<Comparable<*>>().getGenericParameters()[0]

also gives kotlin.Any, which, according to the docs should be kotlin.Nothing. This is because the variance of type T is in in Comparable<in T>.

Combining types

Aside from that, I'd also be interested in a way to "add" multiple types together and do some logic, finding the lowest common parent between two types. Something like:

fun sharedTypes(a: TypeToken<*>, b: TypeToken<*>): List<TypeToken<*>> {
    val aTypes = listOf(a) + a.getSuper()
    val bTypes = listOf(b) + b.getSuper()
    return (aTypes intersect bTypes).toList() + TypeToken.Any
}

fun lowestCommonType(a: TypeToken<*>, b: TypeToken<*>): TypeToken<*> =
    sharedTypes(a, b).first()

lowestCommonType(generic<Int>(), generic<Double>()) == generic<Number>()

but a bit more efficient maybe, and it should be able to calculate lowestCommonType(generic<List<Int>>(), generic<List<Double>>()) correctly. This might also require some variance/bound checks on generics. Specifically, I'd be interested in calculating the combinations of types such as the highlighting of the IDE can do like this:

interface A : Comparable<Double>
interface B : Comparable<Int>
interface C : Comparable<Number>

interface D : List<Double>
interface E : List<Number>

fun function(a: A, b: B, c: C, d: D, e: E) {
    val list1 = listOf(a, c) // inferred as List<Comparable<Double>>
    val list2 = listOf(a, b) // inferred as List<Comparable<Nothing>>
    val list3 = listOf(d, e) // inferred as List<List<Number>>
}
romainbsl commented 1 month ago

Thank you for reaching out.

We have started considering your request. However, this may be challenging due to the non-JVM platform, which has limited APIs compared to JVM.

Currently, this library fulfills its original purpose as it is the core of our dependency injection library, Kodein.

We need to evaluate how the typeOf function has evolved to determine if new capabilities have been added. Unfortunately, we don't have the bandwidth at the moment. Feel free to investigate if you have the time.