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.
Related
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,
) {
...
}
I want to define a Kotlin generic function that takes a lambda as a parameter, but I want to restrict the allowed types of lambdas parameters and return types. How do I do this in Kotlin?
In the following example, I expected the constraint: where T: Base, V: (T) -> Unit to mean that V can only be functions whose first parameter implements Base.
However, I see that the compiler ignores the T: Base part of the constraint and will accept any (Any) -> Unit.
interface Base
fun <T, V> exampleGenericFunction(func: V) where T: Base, V: (T) -> Unit {
println("func is $func")
}
class ImplementsBase : Base
class DoesNotImplementBase
fun main() {
val f1: (ImplementsBase) -> Unit = { }
exampleGenericFunction(f1)
val f2: (DoesNotImplementBase) -> Unit = { }
exampleGenericFunction(f2) // expected this to be a compilation error
}
This unexpected behavior was caused by a bug in the Kotlin >=1.4 compiler.
This bug can be tracked here: https://youtrack.jetbrains.com/issue/KT-48935.
Suppose I have the following code.
open class Parent
class Child: Parent()
fun <T: Parent> foo(obj: T): T = obj
inline fun <T: Parent> foo(block: () -> T): T = foo(block())
fun <T> bar(obj: T): T = obj
inline fun <T> bar(block: () -> T): T = bar(block())
fun main() {
/* Compile error:
Overload resolution ambiguity. All these functions match.
public inline fun <T : Parent> foo (block: () → TypeVariable(T)): TypeVariable(T) defined in examples in file example.kt
public fun <T : Parent> foo (obj: TypeVariable(T)): TypeVariable(T) defined in examples in file example.kt
*/
foo { Child() }
// Works
bar { "something" }
}
The first function call (foo) gives me a compilation error saying the overload resolution is ambiguous. But why is this true since the block is of type () -> T and this isn't a subtype of Parent?
Shouldn't this error occur on the second function call (bar)? Why doesn't it occur?
My guess is that the problem here is with type inference. In each of your examples, you're relying on type inference for two things:
To determine the value of the generic type parameter T, and
To determine the signature of the lambda function.
In the case of foo, the overload resolution depends on knowing the value of the generic parameter T. That's because T is bounded. The actual value of T might affect whether it's valid to call this method or not, depending on whether T turns out to be a subtype of Parent.
The problem is that T needs to be inferred based on the value of the parameter being passed in, and the type of the parameter being passed in needs to be inferred based on some information about which overload has been selected and/or what the value of T is! I suspect this creates a circular dependency that prevents the overload resolution from completing.
You can fix it by providing a value either for the lambda signature or for the generic type parameter:
foo<Parent> { Child() } // works
foo({ Child() } as () -> Parent) // works
Extracting the lambda to a variable fixes it too, because it makes the compiler infer a type for the variable straight away:
val lambda = { Child() } // type is inferred as () -> Child
foo(lambda) // works, because lambda already has an inferred type
The problem doesn't arise with bar because in that case, T is unbounded. No matter what T ends up being, it won't affect whether that particular overload is allowed to be called or not. That means the compiler doesn't need to infer a value for T before performing overload resolution.
Looks like a bug(s) in the compiler (still present in 1.6.0-M1).
Two workarounds here:
Explicitly specify type argument:
foo<Child> { Child() }
Ironically, doing the same with the working part will break it (but the error won't be about ambiguity, the compiler is pretty sure what overload he's gonna call - the wrong one):
/* Compile error:
Type mismatch.
Required: () → String
Found: String
*/
bar<() -> String>({ "something" })
Extract lambda into a separate variable:
val lambda = { Child() }
foo(lambda)
This will fix the broken part as well:
val block = { "something" }
bar<() -> String>(block)
I'm trying to write a Kotlin function which returns a lambda taking a parameter. I'm attempting to use code like the following to do this:
fun <T> makeFunc() : (T.() -> Unit) {
return { t: T ->
print("Foo")
}
}
Note: In the actual program, the function is more complex and uses t.
Kotlin rejects this as invalid, giving an 'Expected no parameters' error at t: T.
However, assigning this lambda to a variable first is not rejected and works fine:
fun <T> makeFunc() : (T.() -> Unit) {
val x = { t: T ->
print("Foo")
}
return x
}
These two snippets seem identical, so why is this the case? Are curly braces after a return statement interpreted as something other than a lambda?
Additionally, IntelliJ tells me that the variable's value can be inlined, whereas this appears to cause the error.
There is a curious moment in the design of functional types and lambda expressions in Kotlin.
In fact, the behavior can be described in these two statements:
Named values of functional types are interchangeable between the ordinary functional type like (A, B) -> C and the corresponding type of function with the first parameter turned into receiver A.(B) -> C. These types are assignable from each other.
So, when you declare a variable that is typed as (T) -> Unit, you can pass it or use it where T.() -> Unit is expected, and vice versa.
Lambda expressions, however, cannot be used in such free manner.
When a function with receiver T.() -> Unit is expected, you cannot place a lambda with a parameter of T in that position, the lambda should exactly match the signature, a receiver and the first parameter cannot be converted into each other:
Shape of a function literal argument or a function expression must exactly match the extension-ness of the corresponding parameter. You can't pass an extension function literal or an extension function expression where a function is expected and vice versa. If you really want to do that, change the shape, assign literal to a variable or use the as operator.
(from the document linked above)
This rule makes lambdas easier to read: they always match the expected type. For instance, there's no ambiguity between a lambda with receiver and a lambda with implicit it that is simply unused.
Compare:
fun foo(bar: (A) -> B) = Unit
fun baz(qux: A.() -> B) = Unit
val f: (A) -> B = { TODO() }
val g: A.() -> B = { TODO() }
foo(f) // OK
foo(g) // OK
baz(f) // OK
baz(g) // OK
// But:
foo { a: A -> println(a); TODO() } // OK
foo { println(this#foo); TODO() } // Error
baz { println(this#baz); TODO() } // OK
baz { a: A -> println(a); TODO() } // Error
Basically, it's the IDE diagnostic that is wrong here. Please report it as a bug to the Kotlin issue tracker.
You are defining a function type () -> Unit on receiver T, there really isn't a parameter to that function, see "()". The error makes sense. Since you define the function type with T as its receiver, you can refer to T by this:
fun <T> makeFunc(): (T.() -> Unit) {
return {
print(this)
}
}
I need to be able to tell the generic type of kotlin collection at runtime. How can I do it?
val list1 = listOf("my", "list")
val list2 = listOf(1, 2, 3)
val list3 = listOf<Double>()
/* ... */
when(list.genericType()) {
is String -> handleString(list)
is Int -> handleInt(list)
is Double -> handleDouble(list)
}
Kotlin generics share Java's characteristic of being erased at compile time, so, at run time, those lists no longer carry the necessary information to do what you're asking. The exception to this is if you write an inline function, using reified types. For example this would work:
inline fun <reified T> handleList(l: List<T>) {
when (T::class) {
Int::class -> handleInt(l)
Double::class -> handleDouble(l)
String::class -> handleString(l)
}
}
fun main() {
handleList(mutableListOf(1,2,3))
}
Inline functions get expanded at every call site, though, and mess with your stack traces, so you should use them sparingly.
Depending on what you're trying to achieve, though, there's some alternatives. You can achieve something similar at the element level with sealed classes:
sealed class ElementType {
class DoubleElement(val x: Double) : ElementType()
class StringElement(val s: String) : ElementType()
class IntElement(val i: Int) : ElementType()
}
fun handleList(l: List<ElementType>) {
l.forEach {
when (it) {
is ElementType.DoubleElement -> handleDouble(it.x)
is ElementType.StringElement -> handleString(it.s)
is ElementType.IntElement -> handleInt(it.i)
}
}
}
You can use inline functions with reified type parameters to do that:
inline fun <reified T : Any> classOfList(list: List<T>) = T::class
(runnable demo, including how to check the type in a when statement)
This solution is limited to the cases where the actual type argument for T is known at compile time, because inline functions are transformed at compile time, and the compiler substitutes their reified type parameters with the real type at each call site.
On JVM, the type arguments of generic classes are erased at runtime, and there is basically no way to retrieve them from an arbitrary List<T> (e.g. a list passed into a non-inline function as List<T> -- T is not known at compile-time for each call and is erased at runtime)
If you need more control over the reified type parameter inside the function, you might find this Q&A useful.