KTable GroupBy Function Overload in Kotlin - kotlin

Attempting to replicate a simple word count example for Kafka Streams.
val groupedByWord: KTable<String, Long> = source
.flatMapValues<Any> { value: String ->
listOf(
value.lowercase(Locale.getDefault()).split("\\W+").toTypedArray()
)
}
.groupBy( { (key: String, word: String) -> word }, Grouped.with(Serdes.String(), Serdes.String()) )
Unfortunately compilation fails due to the second parameter in the groupby being confused with Serialized.
None of the following functions can be called with the arguments supplied:
public abstract fun <KR : Any!> groupBy(p0: KeyValueMapper<in String!, in Any!, TypeVariable(KR)!>!, p1: Grouped<TypeVariable(KR)!, Any!>!): KGroupedStream<TypeVariable(KR)!, Any!>! defined in org.apache.kafka.streams.kstream.KStream
public abstract fun <KR : Any!> groupBy(p0: KeyValueMapper<in String!, in Any!, TypeVariable(KR)!>!, p1: Serialized<TypeVariable(KR)!, Any!>!): KGroupedStream<TypeVariable(KR)!, Any!>! defined in org.apache.kafka.streams.kstream.KStream
I'm looking for suggestions on how to resolve this.

if there's no type change from your original stream Serdes you should be able to just use the first arg and in Kotlin you can also remove the parentheses in this case, like :
groupBy { _, word -> word }

Related

Type inference failed: Not enough information to infer parameter T

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,
) {
...
}

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.

Extension and higher-order functions referencing

I get an overall idea of what is each of these, I wrote piece of code however that I don't quite understand why it works.The thing is callExtensionOnString expects extension function as its parameter, however it doesn't complain if reference to printString is passed.
Is String.() -> Unit just another name for (String) -> Unit type or is it Kotlin compiler that takes some kind of shortcut to fit higher order in extension function?
fun main(args: Array<String>) {
callExtensionOnString(::printString)
}
fun callExtensionOnString(extensionFun: String.() -> Unit) {
"testString".extensionFun()
}
fun printString(str: String) {
println(str)
}
Then there is second thing that seems unclear to me. printString reference is passed once to callExtensionOnString and then again to run function which basically requires String.() -> Unit extension function. Is there difference between these two so that second time :: prefix is omitted?
fun main(args: Array<String>) {
runFunctionOnString(::printString)
}
fun runFunctionOnString(higherOrderFun: (String) -> Unit) {
"testString".run(higherOrderFun)
}
fun printString(str: String) {
println(str)
}
Concerning your first question, these 2 are equivalent:
"testString".extensionFun()
extensionFun("testString")
That's why passing printString method doesn't cause a compilation error.
Concerning the second question, the difference is that highOrderFun is already a reference to a function, while printString is not a function reference (but you can obtain it with the :: operator)

Is the Kotlin documentation correct?

Is the code (show below) correct? It was take from page 63 of the Kotlin-docs.pdf, which is also the last code snippet of https://kotlinlang.org/docs/reference/generics.html
fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<T>
where T : Comparable, T : Cloneable {
return list.filter { it > threshold }.map { it.clone() }
}
Taken by as is, the compiler fails with:
1. One type argument expected for interface Comparable defined in kotlin
2. Type inference failed. Expected type mismatch: inferred type is List but List was expected
3. Cannot access 'clone': it is protected in 'Cloneable'
The first two errors are easily resolved by changing the code to the following:
fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<Any>
where T : Comparable<in T>, T : Cloneable {
return list.filter { it > threshold }.map { it.clone() }
}
I still get the following error:
Cannot access 'clone': it is protected in 'Cloneable'
I'm using Kotlin 1.1.2-release-IJ2017.1-1
Am I missing something? or is there an error in the documentation?
Thanks.
it.clone() returns Any and you get error casting List to List.
So, you have changed it to List.
Your next error is Cannot access clone: it is protected in Cloneable.
This problem can be solved by creating our own Cloneable interface with public method.

How to get generic param class in Kotlin?

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.