Kotlin overload resolution ambiguity in the standard library - kotlin

In Kotlin 0.12.1230 it appears that deprecated APIs are blocking the use of their replacements.
For example, the compiler complains about the following snippet because it "cannot choose among... candidates without completing type inference"
val seq = sequenceOf("1")
val first = seq.firstOrNull()
The candidates are Sequence<T>.firstOrNull and Stream<T>.firstOrNull both of which have identical signatures and Sequence<T> extends Stream<T>. Furthermore, Stream<T> is deprecated in favor of Sequence<T>.
Attempting to resolve the type inference ambiguity, like you see below, results in the compiler complaining about "overload resolution ambiguity".
val seq = sequenceOf("1")
val first = seq.firstOrNull<String?>()
Is there any way to resolve the ambiguity while we wait for deprecated APIs to disappear entirely?
It seems that casting to the least specific type, in this case the deprecated type Stream<T>, accomplishes it, but now my code explicitly depends on a deprecated type when I have no desire to do so:
val seq = sequenceOf("1")
val first = (seq as Stream<String>).firstOrNull()
Hopefully there is a better way?

This seems to be caused by multiple conflicting versions of the Kotlin stdlib on my classpath (caused by a long standing defect in Gradle IntelliJ integration). Once they were version conflict resolved, the compiler no longer complains.

Related

Kotlin - Overload resolution ambiguity. All these functions match

I am trying to add a global state store to my topology, but getting error as
Overload resolution ambiguity. All these functions match.
val stateStoreBuilder =
Stores.keyValueStoreBuilder(
Stores.persistentKeyValueStore("my_global_store"),
Serdes.String(),
Serdes.String()
)
topology.addGlobalStore(
stateStoreBuilder,
"source_name",
KEY_JSON_DE,
VALUE_JSON_DE,
"topic_name",
"processor_name",
{ MyStoreProcessor::class.java },
)
Getting error for addGlobalStore method.
Using below versions in Gradle file :-
kotlin("jvm") version "1.7.10"
kotlin("plugin.spring") version "1.7.10"
implementation("org.apache.kafka:kafka-streams:3.3.1")
implementation("org.springframework.kafka:spring-kafka")
I think the problem is shows in the small letter of the error.
The Kotlin compiler cannot figure out which method to use. In particular to what class it should map the last lambda, to org.apache.kafka.streams.processor.ProcessSupplier or org.apache.kafka.streams.processor.api.ProcessSupplier (notice the later has an api package in the middle).
I reckon that if you cast the lambda, it should work. I've never tried this, but I wonder if changing the line to the following would work
{ MyStoreProcessor::class.java } as org.apache.kafka.streams.processor.api.ProcessSupplier

Kotlin compiler shows compilation error and suggests to "change type from 'T' to 'T"' while there is only one T in the context

I tried to implement some type classes from Haskell but confronted the issue that is probably a bug in the Kotlin compiler.
interface Semigroup<Instance> {
infix fun Instance.assocOp(oother: Instance): Instance
}
inline fun <reified T: Semigroup<T>> Iterable<T>.concat() = this.reduce<T, T> { acc: T, t: T -> acc.assocOp(t) }
The error message is "Expected parameter of type T".
IDEA suggests to "Change type from 'T' to 'T'" (does nothing).
I expect acc to belong to the type T mentioned in generics. But because of some reason compiler tries to find some other type T. I tried to
specify the type explicitly/implicitly
build ignoring IDEA message
change used version of Kotlin compiler (I have tried 1.4.20, 1.4.10, 1.3.72).
Nothing worked.
I suppose that writing the function without reduce (manually) may help to deal with it. Also, writing java code doing the same may help to mitigate the problem. But these solutions are only workarounds for the problem. Is the issue my fault or the compiler bug?
The compiler error clearly is not helpful here. However, it is correct that the code should not compile IMO.
You're defining the method assocOp as a member extension function. The extension applies to any type T, but it's a member of the interface Semigroup<T>.
To call that extension, you need both a receiver or type T and a receiver of type Semigroup<T> (acting as a context).
In your case, the type T both plays the role of the generic type parameter and of the Semigroup<T>, but you still need to have 2 "receivers" for your extension, even if both are the same instance.
Maybe try this:
inline fun <reified T : Semigroup<T>> Iterable<T>.concat(): T =
reduce<T, T> { t1: T, t2: T -> with(t1) { t1.assocOp(t2) } }
The with(t1) { ... } provides a context of type Semigroup<T>, while the t1 used in t1.assocOp(t2) acts as the T receiver.

how to convert Java Map to read it in Kotlin?

I am facing some very basic problem (that never faced in java before) and might be due my lack of knowledge in Kotlin.
I am currently trying to read a YML file. So Im doing it in this way:
private val factory = YamlConfigurationFactory(LinkedHashMap::class.java, validator, objectMapper, "dw")
Best on Dropwizard guide for configurations.
https://www.dropwizard.io/1.3.12/docs/manual/testing.html
So later in my function I do this"
val yml = File(Paths.get("config.yml").toUri())
var keyValues = factory.build(yml)
When using my debugger I can see there is a Map with key->values, just as it should be.
now when I do keyValues.get("my-key")
type inference failed. the value of the type parameter k should be mentioned in input types
Tried this but no luck
var keyValues = LinkedHashMap<String, Any>()
keyValues = factory.build(yml)
The YamlConfigurationFactory requires a class to map to, but I dont know if there is a more direct way to specify a Kotlin class than with the current solution +.kotlin, like
LinkedHashMap::class.java.kotlin
Here it also throws an error.
Ideas?
Well, this is a typical problem with JVM generics. Class<LinkedHashMap> carries no info on what are the actual types of its keys and values, so the keyValues variable always ends up with the type LinkedHashMap<*, *> simply because it can't be checked at compile time. There are two ways around this:
Unsafe Cast
This is how you would deal with the problem in standard Java: just cast the LinkedHashMap<*, *> to LinkedHashMap<String, Any> (or whatever is the actual expected type). This produces a warning because the compiler can't verify the cast is safe, but it is also generally known such situations are often unavoidable when dealing with JVM generics and serialisation.
YamlConfigurationFactory(LinkedHashMap::class.java, ...) as LinkedHashMap<String, Any>
Type Inference Magic
When using Kotlin, you can avoid the cast by actually creating instance of Class<LinkedHashMap<String, Any>> explicitly. Of course, since this is still JVM, you lose all the type info at runtime, but it should be enough to tell the type inference engine what your result should be. However, you'll need a special helper method for this (or at least I haven't found a simpler solution yet), but that method needs to be declared just once somewhere in your project:
inline fun <reified T> classOf(): Class<T> = T::class.java
...
val factory = YamlConfigurationFactory(classOf<LinkedHashMap<String, Any>>(), ...)
Using this "hack", you'll get an instance of LinkedHashMap directly, however, always remember that this is just extra info for the type inference engine but effectively it just hides the unsafe cast. Also, you can't use this if the type is not known at compile type (reified).

Suppress DeprecationLevel.ERROR in Kotlin

I’m using DeprecationLevel.ERROR in my APIs:
#Deprecated(
message = "moved to def()",
replaceWith = ReplaceWith(expression = "def()"),
level = DeprecationLevel.ERROR)
fun abc() = def()
I want a test to ensure callers see this replacement. For example, my test should fail to compile if I accidentally delete the abc() method.
But I can’t find a way to make this compile:
#Test
#Suppress("something")
fun deprecatedAbc() {
abc()
}
For example, #Suppress("DEPRECATION") doesn’t work. Is there something that does?
According to DefaultErrorMessages, you can use #Suppress("DEPRECATION_ERROR").
According to the documentation of #Deprecated:
To help removing deprecated API gradually, the property level could be used. Usually a gradual phase-out goes through the "warning", then "error", then "hidden" or "removed" stages:
First and by default, DeprecationLevel.WARNING is used to notify API consumers, but not to break their compilation or runtime usages.
Then, some time later the deprecation level is raised to DeprecationLevel.ERROR, so that no new Kotlin code can be compiled using the deprecated API.
Code does not compile by design -- and #Suppress only suppresses warnings, not errors (see doc).
So, what you are trying to achieve is not possible. Also, it seems to me as if you're testing a Kotlin feature, rather than your business logic. You might instead try to test the behavior of the replacement.

Why Can't Kotlin Infer The Type For Comparator

Reading the Java interop document about SAM Conversions, I expected the Kotlin function
Collections.sortWith(comparator: kotlin.Comparator<in T> /* = java.util.Comparator<in T> */)
to be able to take a lambda function without needing to explicitly specify the parameter is a Comparator. However the following code gives type inference failed:
val someNumbers = arrayListOf(1, 5, 2)
someNumbers.sortWith({ x, y -> 1 })
whereas:
val someNumbers = arrayListOf(1, 5, 2)
someNumbers.sortWith(Comparator { x, y -> 1 })
compiles and runs correctly
After reading the comments from the Kotlin issue 'SAM for Kotlin classes' I learned a lot regarding the SAM conversion and why typealias was introduced, but not yet why this specific behaviour wasn't solved yet... and I am not the only one as the issue and its comments show.
Summarizing, the SAM conversion was only considered for Java interfaces (compare also this comment). Jetbrains did work on (or still needs to do) a bigger refactoring and tries to solve that issue so that SAMs are also available for Kotlin functions themselves (compare also this comment). They are trying to support SAM conversion for kotlin functions in a separate issue, which could come with 1.3. As I am currently testing 1.3: I did not see anything regarding this yet. So maybe, if you like the SAM conversion as I do, you may want to upvote either SAM for Kotlin classes or SAM conversion for kotlin function or both.
By the way: a very similar example was also used by Ilya Gorbunov using arrayOf().sort.