Type inference failed: Not enough information to infer parameter T - kotlin

I have a function that compares responses from two different endpoints. It looks like this:
suspend fun <I, T> multiplexOrShadow(
request: I,
v1ResponseStringGenerator: KFunction1<T, String> = ::getV1ResponseString,
v2ResponseStringGenerator: KFunction1<T, String> = ::getV2ResponseString,
) {
<Call two different endpoints for request>
val v1str = v1ResponseStringGenerator(v1Response)
val v2str = v2ResponseStringGenerator(v2Response)
<compare v1str and v2str>
}
As you can see, the caller can pass in the function on how to generate response string from the response of the two endpoints. I also have a default function for the response generators. They look like this:
private fun <T> getV1ResponseString(v1Response: T): String {
return v1Response.toString()
}
private fun <T> getV2ResponseString(v2Response: T): String {
return v2Response.toString()
}
This compiles fine in IntelliJ. However, when I run the gradle build, it fails with the error
Type inference failed: Not enough information to infer parameter T in fun <T> getV1ResponseString(v1Response: T): String
Please specify it explicitly.
What am I doing wrong? I am using Kotlin 1.6.10 in my gradle build.

Looks like this is a known issue, which is fixed since Kotlin 1.6.20: https://youtrack.jetbrains.com/issue/KT-12963.
For Kotlin 1.6.10, the workaround is to avoid using the KFunctionN types if you don't need them. For example, if you only need to invoke a function, it's fine to use just the FunctionN type, also denoted with (...) -> ...:
suspend fun <I, T> multiplexOrShadow(
request: I,
v1ResponseStringGenerator: (T) -> String = ::getV1ResponseString,
v2ResponseStringGenerator: (T) -> String = ::getV2ResponseString,
) {
...
}

Related

Compose functions with different context receivers

I started playing with the new context receivers feature. I intend to use that as a "localized dependency injection" to pass client context around. Currently, I have this (ClientProvider is a fun interface):
fun <T> withClient(client: Client, block: ClientProvider.() -> T) =
ClientProvider { client }.block()
This works pretty well in the production code, e.g. I can do
class MyService {
context(ClientProvider)
fun methodUsingClient() {}
}
However, an issue arises when I want to mockk this code in tests. Currently, I have
val myService: MyService = mockk { every { methodUsingClient() } just runs }
This obviously doesn't work because the ClientProvider context is missing. I would like to fix that by composing mockk and withClient. I imagine it could look like this
inline fun <reified T : Any> mockkWithClient(noinline block: context(T) ClientProvider.() -> Unit) =
mockk<T> { withClient(mockk(), block) }
This indeed works at the call site, i.e., the compiler seems to be happy with
val myService: MyService = mockkWithClient { every { methodUsingClient() } just runs }
but the function itself doesn't compile - the compiler complains about the block parameter:
Type mismatch.
Required:
ClientProvider.() → TypeVariable(T)
Found:
context(T) ClientProvider.() → Unit
Intuitively, I would expect that the extra T context wouldn't harm the block usage inside withClient but apparently, it does.
Is there any way how I can define mockkWithClient so it can be used as outlined above?
I got a useful answer to my other, more generally formulated question, and based on that I was able to solve this problem as well (the key point I was missing is that I have to manually pass the receivers to the block):
inline fun <reified T : Any> mockkWithClient(noinline block: context(ClientProvider) T.() -> Unit) =
withMockClient<T> { mockk { block(this#withMockClient, this#mockk) } }
fun <T> withMockClient(block: ClientProvider.() -> T) =
ClientProvider { mockk() }.block()
The intended usage is then as expected:
mockkWithClient { every { methodUsingClient() }
Note, however, the very specific type of block: it's context(ClientProvider) T.() -> Unit. If I read the documentation correctly I should be also able to write context(ClientProvider, T) () -> Unit but that doesn't compile with the message Subtyping relation between context receivers is prohibited. The root cause of this is still unknown to me but my original problem is solved, nevertheless.

Is it possible to use all possible types from reflection as generic type?

I am trying to use reflection to automatically create savedStateHandle for some classes.
fun <T> KProperty1<T, *>.argFrom(handle: SavedStateHandle) =
when (this.returnType.javaType) {
String::class.java -> handle.get<String>(this.name)
Int::class.java -> handle.get<Int>(this.name)
Uri::class.java -> handle.get<Uri>(this.name)
else -> throw RuntimeException("Type not implemented yet")
}
As you see IF the type is for instance String then I want the return type of get function to be String and when doing it this way I have to define every single case manually.
It would be nice if I could just do something like this.
fun <T> KProperty1<T, *>.argFrom(handle: SavedStateHandle) =
handle.get<this.returnType.javaType>(this.name)
You need a type parameter for the return type of the property. Then you can use that type parameter to specify the desired return type of get:
fun <T, V> KProperty1<T, V>.argFrom(handle: SavedStateHandle) =
handle.get<V>(this.name)

Why is this Kotlin class property not shadowed by a method parameter?

Looking at this code in kotlinx.coroutines, I noticed something strange:
/**
* Returns a flow containing the results of applying the given [transform] function to each value of the original flow.
*/
public inline fun <T, R> Flow<T>.map(crossinline transform: suspend (value: T) -> R): Flow<R> = transform { value ->
return#transform emit(transform(value))
}
In the first line, the transform used is clearly this.transform (defined here). Shouldn't the transform declared in the method parameter have been used instead, as it is in the second line?
To test this, I wrote a small class which tries to mimc this behaviour:
// flow.kt
class Flow(val name: String) {
public fun transform (transform: (Any) -> Unit): Flow {
return Flow("transformed")
}
public fun emit(value: Any) {
// do nothing
}
public fun map(transform: (Any) -> Unit): Flow = transform { value ->
return#transform(emit(transform(value)))
}
}
And I get the kind of warning I was expecting when I run kotlinc flow.kt:
flow.kt:12:54: error: type mismatch: inferred type is Unit but Flow was expected
public fun map(transform: (Any) -> Unit): Flow = transform { value ->
^
flow.kt:12:66: error: cannot infer a type for this parameter. Please specify it explicitly.
public fun map(transform: (Any) -> Unit): Flow = transform { value ->
^
(Kotlin version as returned by kotlinc -version is "kotlinc-jvm 1.6.10 (JRE 17.0.1+1)")
So why is it that the code defined in kotlinx.coroutines works? If I understand Kotlin's name shadowing rules correctly it shouldn't have.
In kotlinx.couroutines, the transform parameter takes an argument of type T. Hence, this.transform is used when transform is called with a (Any) -> Unit argument.
In your example, the transform parameter takes an argument of type Any. A (Any) -> Unit is an Any and hence the parameter is being used instead of this.transform. Replacing Any with a type parameter will make your code compile too.

Providing only one type parameter to an extension function with multiple type parameters in Kotlin

Introduction
In Kotlin I have a generic conversion extension function that simplifies conversion of this object of type C to an object of another type T (declared as the receiver) with additional conversion action that treats receiver as this and also provides access to original object:
inline fun <C, T, R> C.convertTo(receiver: T, action: T.(C) -> R) = receiver.apply {
action(this#convertTo)
}
It is used like this:
val source: Source = Source()
val result = source.convertTo(Result()) {
resultValue = it.sourceValue
// and so on...
}
I noticed I often use this function on receivers that are created by parameterless constructors and thought it would be nice to simplify it even more by creating additional version of convertTo() that automates construction of the receiver based on its type, like this:
inline fun <reified T, C, R> C.convertTo(action: T.(C) -> R) = with(T::class.constructors.first().call()) {
convertTo(this, action) // calling the first version of convertTo()
}
Unfortunately, I cannot call it like this:
source.convertTo<Result>() {}
because Kotlin expects three type parameters provided.
Question
Given above context, is it possible in Kotlin to create a generic function with multiple type parameters that accepts providing just one type parameter while other types are determined from the call-site?
Additional examples (by #broot)
Imagine there is no filterIsInstance() in stdlib and we would like to implement it (or we are the developer of stdlib). Assume we have access to #Exact as this is important for our example. It would be probably the best to declare it as:
inline fun <T, reified V : T> Iterable<#Exact T>.filterTyped(): List<V>
Now, it would be most convenient to use it like this:
val dogs = animals.filterTyped<Dog>() // compile error
Unfortunately, we have to use one of workarounds:
val dogs = animals.filterTyped<Animal, Dog>()
val dogs: List<Dog> = animals.filterTyped()
The last one isn't that bad.
Now, we would like to create a function that looks for items of a specific type and maps them:
inline fun <T, reified V : T, R> Iterable<T>.filterTypedAndMap(transform: (V) -> R): List<R>
Again, it would be nice to use it just like this:
animals.filterTypedAndMap<Dog> { it.barkingVolume } // compile error
Instead, we have this:
animals.filterTypedAndMap<Animal, Dog, Int> { it.barkingVolume }
animals.filterTypedAndMap { dog: Dog -> dog.barkingVolume }
This is still not that bad, but the example is intentionally relatively simple to make it easy to understand. In reality the function would be more complicated, would have more typed params, lambda would receive more arguments, etc. and then it would become hard to use. After receiving the error about type inference, the user would have to read the definition of the function thoroughly to understand, what is missing and where to provide explicit types.
As a side note: isn't it strange that Kotlin disallows code like this: cat is Dog, but allows this: cats.filterIsInstance<Dog>()? Our own filterTyped() would not allow this. So maybe (but just maybe), filterIsInstance() was designed like this exactly because of the problem described in this question (it uses * instead of additional T).
Another example, utilizing already existing reduce() function. We have function like this:
operator fun Animal.plus(other: Animal): Animal
(Don't ask, it doesn't make sense)
Now, reducing a list of dogs seems pretty straightforward:
dogs.reduce { acc, item -> acc + item } // compile error
Unfortunately, this is not possible, because compiler does not know how to properly infer S to Animal. We can't easily provide S only and even providing the return type does not help here:
val animal: Animal = dogs.reduce { acc, item -> acc + item } // compile error
We need to use some awkward workarounds:
dogs.reduce<Animal, Dog> { acc, item -> acc + item }
(dogs as List<Animal>).reduce { acc, item -> acc + item }
dogs.reduce { acc: Animal, item: Animal -> acc + item }
The type parameter R is not necessary:
inline fun <C, T> C.convertTo(receiver: T, action: T.(C) -> Unit) = receiver.apply {
action(this#convertTo)
}
inline fun <reified T, C> C.convertTo(action: T.(C) -> Unit) = with(T::class.constructors.first().call()) {
convertTo(this, action) // calling the first version of convertTo()
}
If you use Unit, even if the function passed in has a non-Unit return type, the compiler still allows you to pass that function.
And there are other ways to help the compiler infer the type parameters, not only by directly specifying them in <>. You can also annotate the variable's result type:
val result: Result = source.convertTo { ... }
You can also change the name of convertTo to something like convert to make it more readable.
Another option is:
inline fun <T: Any, C> C.convertTo(resultType: KClass<T>, action: T.(C) -> Unit) = with(resultType.constructors.first().call()) {
convertTo(this, action)
}
val result = source.convertTo(Result::class) { ... }
However, this will conflict with the first overload. So you have to resolve it somehow. You can rename the first overload, but I can't think of any good names off the top of my head. I would suggest that you specify the parameter name like this
source.convertTo(resultType = Result::class) { ... }
Side note: I'm not sure if the parameterless constructor is always the first in the constructors list. I suggest that you actually find the parameterless constructor.
This answer does not solve the stated problem but incorporates input from #Sweeper to provide a workaround at least simplifying result object instantiation.
First of all, the main stated problem can be somewhat mitigated if we explicitly state variable's result type (i.e. val result: Result = source.convertTo {}) but it's not enough to solve the problem in cases described by #broot.
Secondly, using KClass<T> as result parameter type provides ability to use KClass<T>.createInstance() making sure we find a parameterless constructor (if there's any – if there is none, then result-instantiating convertTo() is not eligible for use). We can also benefit from Kotlin's default parameter values to make result parameter type omittable from calls, we just need to take into account that action might be provided as lambda (last parameter of call) or function reference – this will require two versions of result-instantiating convertTo().
So, taking all the above into account, I've come up with this implementation(s) of convertTo():
// version A: basic, expects explicitly provided instance of `receiver`
inline fun <C, T> C.convertTo(receiver: T, action: T.(C) -> Unit) = receiver.apply {
action(this#convertTo)
}
// version B: can instantiate result of type `T`, supports calls where `action` is a last lambda
inline fun <C, reified T : Any> C.convertTo(resultType: KClass<T> = T::class, action: T.(C) -> Unit) = with(resultType.createInstance()) {
(this#convertTo).convertTo(this#with, action)
}
// version C: can instantiate result of type `T`, supports calls where `action` is passed by reference
inline fun <C, reified T : Any> C.convertTo(action: T.(C) -> Unit, resultType: KClass<T> = T::class) = with(resultType.createInstance()) {
(this#convertTo).convertTo(T::class, action)
}
All three versions work together depending on a specific use case. Below is a set of examples explaining what version is used in what case.
class Source { var sourceId = "" }
class Result { var resultId = "" }
val source = Source()
fun convertX(result: Result, source: Source) {
result.resultId = source.sourceId
}
fun convertY(result: Result, source: Source) = true
fun Source.toResultX(): Result = convertTo { resultId = it.sourceId }
fun Source.toResultY(): Result = convertTo(::convertX)
val result0 = source.convertTo(Result()) { resultId = it.sourceId } // uses version A of convertTo()
val result1: Result = source.convertTo { resultId = it.sourceId } // uses version B of convertTo()
val result2: Result = source.convertTo(::convertX) // uses version C of convertTo()
val result3: Result = source.convertTo(::convertY) // uses version C of convertTo()
val result4: Result = source.toResultX() // uses version B of convertTo()
val result5: Result = source.toResultY() // uses version C of convertTo()
P.S.: As #Sweeper notices, convertTo might not be a good name for the result-instantiating versions (as it's not as readable as with basic version) but that's a secondary problem.

obtain class from nested type parameters in kotlin

I have a val built like this
val qs = hashMapOf<KProperty1<ProfileModel.PersonalInfo, *> ,Question>()
How can I obtain the class of ProfileModel.PersonalInfo from this variable?
In other words what expression(involving qs of course) should replace Any so that this test passes.
#Test
fun obtaionResultTypeFromQuestionList(){
val resultType = Any()
assertEquals(ProfileModel.PersonalInfo::class, resultType)
}
Thank you for your attention
There is no straight way to get such information due to Java type erasure.
To be short - all information about generics (in your case) is unavailable at runtime and HashMap<String, String> becomes HashMap.
But if you do some changes on JVM-level, like defining new class, information about actual type parameters is kept. It gives you ability to do some hacks like this:
val toResolve = object : HashMap<KProperty1<ProfileModel.PersonalInfo, *> ,Question>() {
init {
//fill your data here
}
}
val parameterized = toResolve::class.java.genericSuperclass as ParameterizedType
val property = parameterized.actualTypeArguments[0] as ParameterizedType
print(property.actualTypeArguments[0])
prints ProfileModel.PersonalInfo.
Explanation:
We define new anonymous class which impacts JVM-level, not only runtime, so info about generic is left
We get generic supperclass of our new anonymous class instance what results in HashMap< ... , ... >
We get first type which is passed to HashMap generic brackets. It gives us KProperty1< ... , ... >
Do previous step with KProperty1
Kotlin is tied to the JVM type erasure as well as Java does. You can do a code a bit nice by moving creation of hash map to separate function:
inline fun <reified K, reified V> genericHashMapOf(
vararg pairs: Pair<K, V>
): HashMap<K, V> = object : HashMap<K, V>() {
init {
putAll(pairs)
}
}
...
val hashMap = genericHashMapOf(something to something)