Why cannot be `const val` used in build.gradle.kts - kotlin

I'd like to define a version constant in guild.gradle.kts file so that it can be used even in the plugins block. The plugins block requires restricted syntax:
«plugin version» must be constant, literal, strings
Following the restriction I tried to define the a version constant:
const val kotlinVersion = "1.3.72"
plugins {
java
kotlin("jvm") version kotlinVersion
}
However this fails with message
Const 'val' are only allowed on top level or in objects
even though the declaration seem to meet all const requirements. Why cannot be const val used in build.gradle.kts?

Even though it seems like your build script is top level, it isn't. The gradle docs mentions this when explaining the lifecycle of the build:
Finally, evaluate each Project by executing its build.gradle file, if present, against the project.
(source) This means that in your kotlin build scripts, the receiver type (i.e. this) is KotlinBuildScript which is eventually a subclass of Project. I don't know the details about how it's evaluated, but I can imagine it would be equivalent to what you can do in Kotlin with receiver types:
fun Project.evaluate(buildScript: Project.() -> Unit) = this.evaluate()
So your build script is really just the inside of a closure, hence why you can't use const val.

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

How to substitute dependency for a specific variant of Kotlin MPP dependency via attribute selectors?

This question has nothing to do with Kotlin multiplatform (Kotlin MPP) in that sense that from gradle point of view, Kotlin MPP configuration is just a particular case of multivariant build.
So, imagine I want to redefine particular variant of dependency, for instance, I want to have a javascript module redefined. This approach works perfectly well:
substitute(module("org.shabunc.demo:lib-js")).using(project(":lib-js"))
However it works because I know that for a common dependency "org.shabunc.demo" the "org.shabunc.demo-js" corresponds to the js variant of dependency.
I've decided to experiment with more advanced approach where I'm redefining the variant dependency, and I've tried to do something like this:
val kotlinPlatformTypeAttr = Attribute.of("org.jetbrains.kotlin.platform.type", KotlinPlatformType::class.java)
...
substitute(variant(module("org.shabunc.demo:lib")) {
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, "kotlin-api"))
attribute(kotlinPlatformTypeAttr, KotlinPlatformType.js)
}
}).using(variant(project(":lib-js")) {
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, "kotlin-api"))
attribute(kotlinPlatformTypeAttr, KotlinPlatformType.js)
}
})
However this not working as expected, the compileKotlinMetadata is not resolving any entity defined in a common part of org.shabunc.demo:lib
My question is pretty straightforward: How can I redefine specific variant of Kotlin MPP dependency without relying on its name rather than on the name of common dependency?

Annotation class does not validate input as enum in Kotlin

I'd like to create annotation instead of enum use it for when statement
#Retention(AnnotationRetention.SOURCE)
#IntDef(SELECT.toLong(), WEAR.toLong(), BAND.toLong())
annotation class CurrentState
companion object {
const val SELECT = 0
const val WEAR = 1
const val BAND = 2
}
private fun handleFragment(#CurrentState state:Int) {
val fragment:Fragment =
when(state){
SELECT -> SelectDeviceFragment.newInstance()
WEAR -> ConnectWatchFragment.newInstance()
BAND -> SelectDeviceFragment.newInstance()
}
From my understanding, this code should prevent me from performing following code:
handleFragment(5)
and when statement should not ask me to add else statement, as expected from enum
What I'm doing wrong or misunderstand?
From https://discuss.kotlinlang.org/t/intdef-and-stringdef-not-being-checked-at-compile-time/7029:
This checking doesn’t come from the compiler, but from Android lint. Work to make android lint language independent is being done, but if I’m not mistaken you’ll need a newer version of Android Studio for it.
And Android Studio 3.1 blog post mentions lint checks for Kotlin as a feature (though it doesn't say whether this check specifically is supported).
The #IntDef annotation is part of the Android framework. The compiler is not aware of any specific semantics of this annotation, and is not able to use it for checking the exhaustiveness of when statements.
Moreover, even with the annotation, you can call handleFragment(5). Such code will not be a compiler error, it will only be reported as a lint warning.

Jinq in Kotlin - how to convert lambda into java SerializedLambda?

Can I have serializable lambda in Kotlin? I am trying to use Jinq library from Kotlin, but it requires serializable lambdas. Is there any syntax that makes it possible?
Update:
My code:
var temp=anyDao.streamAll(Task::class.java)
.where<Exception,Task> { t->t.taskStatus== TaskStatus.accepted }
.collect(Collectors.toList<Task>());
I am getting this error:
Caused by: java.lang.IllegalArgumentException:
Could not extract code from lambda.
This error sometimes occurs because your lambda references objects that aren't Serializable.
All objects referenced in lambda are serializable (code results in no errors in java).
Update 2
After debugging it seems that kotlin lambda isn't translated into java.lang.invoke.SerializedLambda which is required by Jinq to get information from. So the problem is how to convert it to SerializedLambda.
I'm the maker of Jinq. I haven't had the time to look at Kotlin-support, but based on your description, I'm assuming that Kotlin compiles its lambdas into actual classes or something else. As such, Jinq would probably need some special code for cracking open Kotlin lambdas, and it may also need special code for handling any unusual Kotlin-isms in the generated code. Jinq should be capable of handling it because it was previously retrofitted to handle Scala lambdas.
If you file an issue in the Jinq github about it, along with a small Kotlin example (in both source and .class file form), then I can take a quick peek at what might be involved. If it's small, I can make those changes. Unfortunately, if it looks like a lot of work, I don't think I can really justify putting a lot of resources into adding Kotlin support to Jinq.
I have no experience on Jinq, but according to the implementation in GitHub and my experience of using Java Library in Kotlin.
ref: https://github.com/my2iu/Jinq/blob/master/api/src/org/jinq/orm/stream/JinqStream.java
You can always fall back to use the native Java Interface in Kotlin.
var temp = anyDao.streamAll(Task::class.java)
.where( JinqStream.Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
// Alternatively, You you can import the interface first
import org.jinq.orm.stream.JinqStream.*
...
// then you can use Where instead of JinqStream.Where
var temp = anyDao.streamAll(Task::class.java)
.where(Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
Or make a custom extension to wrap the implementation
fun JinqStream<T>.where(f: (T) -> Boolean): JinqStream<T> {
return this.where(JinqStream.Where<T,Exception> { f(it) })
}
Disclaimer: The above codes have not been tested.

Kotlin overload resolution ambiguity in the standard library

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.