typeskotlinsignaturemutablemap

Complex MutableMap type


I'm getting a little confused here.

So, I have a mutableMap of type

val converters = mutableMapOf<String, Pair<KFunction<ElemBase>, x>>()

where I can't figure out x

I need to add to this map several pairs based on a constructor and a method, such as:

converters["Camera"] = ::Camera to Structure::convertCamera

Where the first field is the reference to a constructor extending ElemBase, such as:

Camera : ElemBase

and the second is a method on the Structure class with a reference to a nullable ElemBase class:

fun convertCamera (dest: KMutableProperty0<Camera?>)

when x

KFunction<*> works, but I needs something more specific, where Receiver and argument are defined.

I tried a couple of attempts, such as

KFunction<Structure.(KProperty1<Structure, ElemBase>) -> Unit>

or

KFunction<(KMutableProperty0<ElemBase>) -> Unit>

or

KFunction<Structure.(KMutableProperty0<ElemBase?>) -> Unit>

I always get error whenever I try to add anything to converters

Error:(276, 9) Kotlin: Type inference failed: Cannot infer type parameter V in operator inline fun <K, V> MutableMap<K, V>.set(key: K, value: V): Unit
None of the following substitutions
receiver: MutableMap<String, Pair<Int, KFunction<Structure.(KMutableProperty0<ElemBase?>) -> Unit>>>  arguments: (String,Pair<Int, KFunction<Structure.(KMutableProperty0<ElemBase?>) -> Unit>>)
receiver: MutableMap<String, Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>>  arguments: (String,Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>)
can be applied to
receiver: MutableMap<String, Pair<Int, KFunction<Structure.(KMutableProperty0<ElemBase?>) -> Unit>>>  arguments: (String,Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>)

but no lucks so far

It's interesting to notice that if I assign ::Camera to Structure::convertCamera to a variable, I get the following type: Pair<KFunction0<ElemBase>, KFunction2<Structure, @ParameterName KMutableProperty0<ElemBase>, Unit>>

But I do not have KFunction0 neither KFunction2... what the hell?

Edit: found this

Edit2: if I manually import KFunction0 and KFunction2 it seems it doesnt complain anymore about them. But if I click on them, it cannot resolve..

I tried to import manually both, KFunction0 and KFunction2, copying the type I saw when I assign the pair to a var and it looks like this is working:

val converters = mutableMapOf<String, Pair<KFunction0<Camera>, KFunction2<Structure, KMutableProperty0<Camera>, Unit>>>()

But not the corresponding ElemBase version:

val converters = mutableMapOf<String, Pair<KFunction0<ElemBase>, KFunction2<Structure, KMutableProperty0<ElemBase>, Unit>>>()

Error:(277, 9) Kotlin: Type inference failed: Cannot infer type parameter V in operator inline fun <K, V> MutableMap<K, V>.set(key: K, value: V): Unit
None of the following substitutions
receiver: MutableMap<String, Pair<KFunction0<ElemBase>, KFunction2<Structure, KMutableProperty0<ElemBase>, Unit>>>  arguments: (String,Pair<KFunction0<ElemBase>, KFunction2<Structure, KMutableProperty0<ElemBase>, Unit>>)
receiver: MutableMap<String, Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>>  arguments: (String,Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>)
can be applied to
receiver: MutableMap<String, Pair<KFunction0<ElemBase>, KFunction2<Structure, KMutableProperty0<ElemBase>, Unit>>>  arguments: (String,Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName KMutableProperty0<Camera>, Unit>>)

Although I still don't get what's going on with KFunction*, why they seems to not exist althought once manually imported, the compiler accept them

In case the design smells, I'm porting some c++ code here and I'd like to stick to the original structure if possible

Edit3: ok, I'll probably change design, but I'd like to find out what's going on anyway, for the sake of curiosity


Solution

  • If you write down the following code in IntelliJ:

    val map = mutableMapOf("Camera" to (::Camera to Structure::convertCamera))
    

    You can then invoke the "specify type explicitly" intention action on the assignment, or press "Ctrl + Q" on the variable to bring up its type information which is inferred.

    This gets you the following:

    val map: MutableMap<String, Pair<KFunction0<Camera>, KFunction2<Structure, @ParameterName(name = "dest") KMutableProperty0<Camera?>, Unit>>>

    KFunction0 and KFunction2 here are the some classes that Kotlin compiles functions types to (ones that have 0 and 2 parameters, respectively). From the Kotlin in Action book:

    The Kotlin standard library defines a series of interfaces, corresponding to different numbers of function arguments: Function0<R> (this function takes no arguments), Function1<P1, R> (this function takes one argument), and so on. Each interface defines a single invoke method, and calling it will execute the function.

    You can replace these with the function types instead, and remove some of the unnecessarily specific info, like this:

    val map: MutableMap<String, Pair<() -> Camera, (Structure, KMutableProperty0<Camera?>) -> Unit>>

    Since you wanted ElemBase to be in the type instead of the concrete Camera type, you can do this:

    val map3: MutableMap<String, Pair<() -> ElemBase, (Structure, KMutableProperty0<Camera?>) -> Unit>>

    However, you can't change KMutableProperty0<Camera> to KMutableProperty0<ElemBase>, since this type is invariant, and so this would break the assignment in your example. Just like MutableList<T>, it both accepts and produces its generic type, therefore it can't accept a subtype or a supertype in its generic parameter instead of the one required.