Was playing a bit with arrow library for Kotlin and found this error right out of the documentation https://arrow-kt.io/docs/optics/ . What am I doing wrong?
Unresolved reference: company
the code is next, so it is not compiling due to an error in reference
package com.app
import arrow.optics.Optional
import arrow.optics.optics
#optics
data class Street(val number: Int, val name: String) {
companion object
}
#optics
data class Address(val city: String, val street: Street) {
companion object
}
#optics
data class Company(val name: String, val address: Address) {
companion object
}
#optics
data class Employee(val name: String, val company: Company) {
companion object
}
fun main() {
// an immutable value with very nested components
val john = Employee("John Doe", Company("Kategory", Address("Functional city", Street(42, "lambda street"))))
// an Optional points to one place in the value
val optional: Optional<Employee, String> = Employee.company.address.street.name
// and now you can modify into a new copy without nested 'copy's!
optional.modify(john, String::toUpperCase)
}
my dependencies are next
//region Arrow
implementation("io.arrow-kt:arrow-core:$arrow_version")
implementation("io.arrow-kt:arrow-fx-coroutines:$arrow_version")
implementation("io.arrow-kt:arrow-optics:$arrow_version")
//endregion
Your Gradle configuration seems to be missing some of the required Google KSP setup. You can find it in the Arrow Optics Setup section of the website, and below.
plugins {
id("com.google.devtools.ksp") version "$googleKspVersion"
}
dependencies {
ksp("io.arrow-kt:arrow-optics-ksp-plugin:$arrowVersion")
}
You also need to make IDEA aware of the generated sources, or it will not be able to correctly pick up the code for code highlighting and syntax reporting. The setup is explained on the official Kotlin Website, and shown below.
kotlin {
sourceSets.main {
kotlin.srcDir("build/generated/ksp/main/kotlin")
}
sourceSets.test {
kotlin.srcDir("build/generated/ksp/test/kotlin")
}
}
Add the code below (*.gradle.kts) in your build script.
It will register a new source dir so that your IDE will see generated extensions. This works regardless of the number of flavors and build types you have.
android {
// ...
androidComponents.onVariants { variant ->
val name = variant.name
sourceSets {
getByName(name).kotlin.srcDir("${buildDir.absolutePath}/generated/ksp/${name}/kotlin")
}
}
}
Related
I want to tinker with Kotlin Compose for Web a bit.
In some of my past web projects, I made use of some web components of the Clarity Design System (CDS).
In a JavaScript or TypeScript project,
you first need to install both npm packages#cds/core and #cds/city.
Secondly, you have to include some global stylesheets, e.g. via HTML or sass-import.
For each component you want to use, you need to import the corresponding register.js.
Lastly, you can include the component in your HTML like any other tag:
<cds-button>Click me!</cds-button>
I tried to replicate the steps with Kotlin Compose for Web, but wasn't able to get it to work.
Any help appreciated!
Okay, I've got it to work now, which included several steps.
Install npm dependencies
kotlin {
...
sourceSets {
val jsMain by getting {
dependencies {
// dependencies for Compose for Web
implementation(compose.web.core)
implementation(compose.runtime)
// dependencies for Clarity Design System
implementation(npm("#cds/core", "5.6.0"))
implementation(npm("#cds/city", "1.1.0"))
// dependency for webpack - see step 3
implementation(npm("file-loader", "6.2.0"))
}
}
...
}
}
Enable css support
This seems to be required, in order to include the global stylesheets.
kotlin {
js(IR) {
browser {
...
commonWebpackConfig {
cssSupport.enabled = true
}
}
...
}
...
}
Add support for .woff2 files included in stylesheet of Clarity
The stylesheet of CDS include font files of type .woff2, whose support in webpack must be configured.
This can be achieved by creating the file webpack.config.d/support-fonts.js
at the project root with the following content:
config.module.rules.push({
test: /\.(woff(2)?|ttf|eot|svg|gif|png|jpe?g)(\?v=\d+\.\d+\.\d+)?$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'fonts/'
}
}]
});
Include global stylesheets
external fun require(module: String): dynamic
fun main() {
require("modern-normalize/modern-normalize.css")
require("#cds/core/global.min.css")
require("#cds/core/styles/module.shims.min.css")
require("#cds/city/css/bundles/default.min.css")
...
}
Import register.js for desired web component
external fun require(module: String): dynamic
fun main() {
...
require("#cds/core/button/register.js")
...
}
Create #Composable for the web component
Sadly this solution makes use of APIs marked as #OptIn(ComposeWebInternalApi::class),
which stands for "This API is internal and is likely to change in the future".
Any hints on how this may be implemented without relying on internal APIs are appreciated.
#Composable
fun CdsButton(
status: CdsButtonStatus = CdsButtonStatus.Primary,
attrs: AttrBuilderContext<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) = TagElement(
elementBuilder = CdsElementBuilder("cds-button"),
applyAttrs = {
if (attrs != null) apply(attrs)
attr("status", status.attributeValue)
},
content = content
)
/**
* This is a copy of the private class org.jetbrains.compose.web.dom.ElementBuilderImplementation
*/
internal class CdsElementBuilder<TElement : Element>(private val tagName: String) : ElementBuilder<TElement> {
private val element: Element by lazy {
document.createElement(tagName)
}
override fun create(): TElement = element.cloneNode() as TElement
}
sealed interface CdsButtonStatus {
object Primary : CdsButtonStatus
...
}
internal val CdsButtonStatus.attributeValue
get() = when (this) {
CdsButtonStatus.Primary -> "primary"
...
}
Make us of your #Composable!
fun main() {
...
renderComposable(rootElementId = "root") {
CdsButton(
status = CdsButtonStatus.Success
) {
Text("It works! :-)")
}
}
}
I am trying to make a simple server which gives serialized List in JSON. The List to be serialized is the example in the official blog post's Polymorphic serialization section.
But with the ktor's serialization feature, I get the following exception.
21:53:25.536 [nioEventLoopGroup-4-1] ERROR ktor.application - Unhandled: GET - /
java.lang.IllegalStateException: Serializing collections of different element types is not yet supported. Selected serializers: [DirectMessage, BroadcastMessage]
at io.ktor.serialization.SerializerLookupKt.elementSerializer(SerializerLookup.kt:71)
Since sealed class is a key feature to choose Kotlin, I really wonder why this is not supported.
Are there any good reasons for ktor-serialization not supporting this? Or should I post an issue for removing this check from SerializerLookup.kt?
I made this code by choosing New Project > Kotlin > Application in IntelliJ. The modified code is shown below.
My server.kt:
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.serialization.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import kotlinx.serialization.Serializable
#Serializable
sealed class Message {
abstract val content: String
}
#Serializable
data class BroadcastMessage(override val content: String) : Message()
#Serializable
data class DirectMessage(override val content: String, val recipient: String) : Message()
val data: List<Message> = listOf(
DirectMessage("Hey, Joe!", "Joe"),
BroadcastMessage("Hey, all!")
)
fun main() {
embeddedServer(Netty, port = 8080, host = "127.0.0.1") {
install(ContentNegotiation) {
json()
}
routing {
get("/") {
call.respond(data)
}
}
}.start(wait = true)
}
My build.gradle.kts:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.4.10"
application
kotlin("plugin.serialization") version "1.4.10"
}
group = "com.example.ktor.serialization"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
jcenter()
maven {
url = uri("https://dl.bintray.com/kotlin/ktor")
}
maven {
url = uri("https://dl.bintray.com/kotlin/kotlinx")
}
}
dependencies {
testImplementation(kotlin("test-junit5"))
implementation("io.ktor:ktor-server-netty:1.4.1")
implementation("io.ktor:ktor-html-builder:1.4.1")
implementation("io.ktor:ktor-serialization:1.4.1")
implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.7.2")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0")
implementation("ch.qos.logback:logback-classic:1.2.3")
}
tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "11"
}
application {
mainClassName = "ServerKt"
}
As said in the stacktrace this is not supported yet., so it might come someday.
However, a workaround is still possible for such a case.
The issue is from Ktor, not Kotlinx Serialization.
So, you can serialize your data as JSON, and then send them as a response, like here:
fun Application.module(testing: Boolean = false) {
install(ContentNegotiation) { json() }
routing {
get("/") {
val data: List<Message> = listOf(
DirectMessage("Hey, Joe!", "Joe"),
BroadcastMessage("Hey, all!")
)
val string = Json.encodeToString(data)
call.respondText(string, contentType = ContentType.Application.Json)
}
}
}
#Serializable
sealed class Message {
abstract val content: String
}
#Serializable
data class BroadcastMessage(override val content: String) : Message()
#Serializable
data class DirectMessage(override val content: String, val recipient: String) : Message()
The reason is that we don't have the particular type information and can only analyze instance classes in runtime. Analyzing and runtime type intersection is not an easy task and for sure it will be very inefficient that is unacceptable on server-side.
Using typeOf could potentially help, but we haven't analyzed the performance impact of such a change (including allocation profile). The other reason is that we didn't know about typeOf (it didn't exist) and call.respond has been designed without it so this change will for sure be a breaking change.
I'm trying to retrieve the instances of my properties, like the example below:
data class DataClass(
val inner: InnerClass
)
data class AnotherDataClass(
val annotatedProperty: String,
val dataClass: DataClass
)
instance = AnotherDataClass("prop", DataClass("anotherprop"))
instance::class.memberProperties.forEach {
// how to retrieve the instance of the properties here?
}
Use kotlin's kClass to access the properties. when you are iterating over the properties you can check the type of property and if it is in fact of type DataClass then you cast it to DataClass and access its values as usual.
var instance = AnotherDataClass("prop", DataClass("AnotherProperty"))
instance.javaClass.kotlin.memberProperties.forEach{
var propertyValue = it.get(instance)
when(propertyValue){
// if propertyValue is of DataClass then
// access its internal fields as you like
is DataClass -> println(propertyValue.inner)
else -> println(propertyValue)
}
}
I was able to solve it in a not-very-beautiful way:
instance::class.memberProperties.forEach {
instance.javaClass.getMethod("get${it.name.capitalize()}").invoke(instance)
}
You can use call here.
import kotlin.reflect.full.memberProperties
data class Member(
val id: Int,
val name: String,
)
fun main(){
val member = Member(1, "kyakya")
println(member.id)
println(member.name)
member::class.memberProperties.forEach {
val call = it.call(member)
println("${it.name} : ${call}")
}
}
Gradle:
plugins {
// Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin.
id("org.jetbrains.kotlin.jvm") version "1.5.0"
}
dependencies {
// Kotlin
// Align versions of all Kotlin components
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
//
implementation("org.jetbrains.kotlin:kotlin-reflect")
}
Our project decided to make the "jump" and move to open implementations, and grabbing the opportunity to move the Kotlin Java compatibility to version 11.
It introduced several 3rd-party library compatibility challenges, but I was able to solve them all.
Now I use Kotlin 1.3.50 (with the new JSR223 engine) with Java 11 target and builds and runs correctly.
plugins {
kotlin("jvm") version "1.3.50"
}
dependencies {
compile(kotlin("stdlib"))
implementation(kotlin("scripting-jsr223-embeddable"))
}
configure<JavaPluginConvention> {
sourceCompatibility = JavaVersion.VERSION_11
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "11"
}
The application uses complex DSL scripts in the form of kts. They worked perfectly prior upgrading to 11, but fails to evaluate since switching to version 11.
When the code calls the eval:
val engine = ScriptEngineManager().getEngineByExtension("kts")!!
val building = engine.eval(src) as Building
it throws class version incompatibility exception:
javax.script.ScriptException: Cannot inline bytecode built
with JVM target 11 into bytecode that is being built with
JVM target 1.8. Please specify proper '-jvm-target' option
...
From this error, I can't determine whether the script engine compiles the script into Java 8 class and then tries to add the result to the core code; or the script tries to use a class which is not in Java 11 format.
How can fix this incompatibility or how could I debug all the project (including 3rd party) classes of their class file version?
Note: The DSL is too complex to give a clear and simple example, however I was able to eval the script of "2+2" without problem.
EDIT 1: A simplified example
After a long lets-comment-out session, I was able to locate the problem: it fails when I use a reified inline function in the script. This discovery made it possible to create a simple example:
package hu.app
import javax.script.ScriptEngineManager
import kotlin.reflect.KClass
/**
* A class representing a type-value pair
*/
class TypedProperty<T : Any>(val type: KClass<T>, initialValue: T? = null) {
var value: T? = initialValue
companion object {
inline fun <reified T : Any> create(initialValue: T? = null) = TypedProperty(T::class, initialValue)
}
}
/**
* The root object the DSL creates. Body omitted for simplicity.
*/
class Building {
// Class body is omitted for simplicity
}
/**
* The simple DSL
*/
#DslMarker
annotation class MyDsl
#MyDsl
class PropertyBuilder constructor() {
#MyDsl
val properties: Map<String, TypedProperty<*>> = mutableMapOf()
/**
* An extension function for String allowing the creation of a freely typed, but typed properties.
* This function causes the problem!
*/
#MyDsl
inline infix fun <reified T : Any> String.set(value: T?) {
val p = (properties as MutableMap).getOrPut(this, { TypedProperty.create(value) })
if (T::class == p.type)
(p as TypedProperty<T>).apply { this.value = value }
else
throw RuntimeException("Property '$this' is reassigned with different type. Original: ${p.type.simpleName}, new: ${T::class.simpleName}")
}
}
// Root of the DSL
#MyDsl
fun building(id: String = "Unnamed", op: BuildingBuilder.() -> Unit) = BuildingBuilder(id).apply(op).build()
// An interface for DSL classes with property support
#MyDsl
interface HasPropertiesBuilder {
#MyDsl
val properties: PropertyBuilder
#MyDsl
fun properties(op: PropertyBuilder.() -> Unit) = properties.op()
}
// The builder of the root object: it has properties
class BuildingBuilder(val id: String) : HasPropertiesBuilder {
override val properties = PropertyBuilder()
// This function body is omitted for simplification
fun build() : Building = Building()
}
fun main() {
// Initialize the engine (new 1.3.50 engine is used)
val engine = ScriptEngineManager().getEngineByExtension("kts")!!
// Evaluation
val res = engine.eval("""
import hu.pmi.autoparking.app.*
building {
properties {
"X" set 42
}
}
""".trimIndent()) as Building
}
In the above example, there is an extension function on String letting to create typed properties. It has reified type parameter, so it should be inline. This method cause the exception: if the "X" set 42 line is removed, the script compiles.
Digging deeper, now it is clear, that the script engine compiles into Java 8 and therefor, when it tries to inject the inline function which is in Java 11 class format, it fails.
So the new question: how could I tell the JSR223 kotlin engine to compile to version 11?
I'm using AssertJ in Kotlin and tried to use an AssertJ-Condition.
The constructor is defined like this:
Condition(Predicate<T> predicate, String description, Object... args)
See http://joel-costigliola.github.io/assertj/core-8/api/org/assertj/core/api/Condition.html
But I can't get the creation right. I tried the following (and some more, which I omitted for brevity):
Condition<File>({ it.name.startsWith("functional_questions") }, "Description")
with this error:
Condition<File>({ file -> file.name.startsWith("functional_questions") }, "Description")
with this error:
How can i succeed?
The best I can come up with is:
Condition<File>(Predicate<File> { file -> file.name.startsWith("functional_questions") }, "description")
Edit: Your code does not work because Kotlin lambda by default does not implement the required interface. If you run the following code:
val predicate1 = { file: File -> file.name.startsWith("functional_questions") }
val predicate2 = Predicate<File> { file -> file.name.startsWith("functional_questions") }
predicate1::class.java.interfaces.forEach { println(it) }
println() //new line to separate outputs
predicate2::class.java.interfaces.forEach { println(it) }
you get the following output:
interface kotlin.jvm.functions.Function1
interface java.util.function.Predicate
The difference is obvious.
AssertJ has no special support for Kotlin as far as I can see, therefore only the Java Predicate is supported.
See more here regarding why we cannot pass kotlin lambdas as functional interfaces: https://stackoverflow.com/a/33610615/5335131
Here are two ways to make it work:
#Test fun assertj() {
Condition<File>(Predicate { it.name.startsWith("functional_questions") }, "Description")
condition<File>("Description") { it.name.startsWith("functional_questions") }
}
fun <T> condition(description: String, predicate: (T) -> Boolean) =
Condition<T>(Predicate<T> { predicate(it) }, description)
First with a explicit Predicate and second with a helper.