Hello, I’m the author of some Kotlin libraries that I have been developing in the past months and that are going to reach 1.0 soon. Even when this post is pretty big, it is really important to me to be able to use all kotlin language targets:
- GitHub - soywiz/korio: Korio: Kotlin cORoutines I/O : Virtual File System + Async/Sync Streams + Async TCP Client/Server + WebSockets for Multiplatform Kotlin 1.3 - Asynchronous I/O / VFS
- GitHub - korlibs-archive/korma: Mathematics library focused on geometry for Multiplatform Kotlin 1.3 - Mathematics (geometry, triangulation, shape operations…)
- GitHub - korlibs-archive/korte: Kotlin cORoutines Template Engine for Multiplatform Kotlin - Template engine (compatible with twig/django and liquid)
- GitHub - korlibs-archive/korim: Korim: Kotlin cORoutines IMaging, Bitmap and Vector graphics for Multiplatform Kotlin - Imaging (native file loading/custom decoders, bitmap and vectors rasterizing)
- GitHub - korlibs-archive/korau: Kotlin cORoutines AUdio - Pure Kotlin WAV, MP3 and OGG vorbis decoders - Audio (mp3, ogg decoding + playing)
- GitHub - korlibs-archive/korag: *MOVED* https://github.com/korlibs/korui Kotlin cORoutines Accelerated Graphics - Accelerated Graphics (aka OpenGL, DirectX, whatver…)
- GitHub - korlibs-archive/korgw: Portable Game Window for Kotlin for JVM, Android and HTML5 - Portable UIs compatible with AWT/HTML/Android and KorGE
- GitHub - korlibs/korge: KorGE Game Engine. Multiplatform Kotlin Game Engine & Korlibs - Asynchronous 2D Game Engine with scenes and dependency injection
There are some inter-dependencies between those libraries. But they are pure kotlin libraries. And they are designed to be portable and to hook with platform-specific implementations.
My current approach is to generate JVM code and transcompile to other targets using my jtransc transcompiler. Creating the code and debugging in the JVM and then creating compiling for targets that just work. But I’m eager to use direct kotlin targets, specially now that there is a JS target and a LLVM target in the works.
I need some help figuring out how to do this (and if there is documentation related to this, please I really appreciate some links). And give feedback about my current workflow / whishlist.
Reflection
The most important part for me here is reflection. Right now I’m using it for three things:
Service Loader
I’m using service loader to find implementations. I have pure kotlin core libraries. For example korag-core, that just provides abstract classes and common classes, and then I’m using a Service Loader (compatible with jtransc) to find implementations in the final injected libraries. In my case I’m using. Something like this that JTransc interprets in the file:
korag/korag/resources/META-INF/services/com.soywiz.korag.AGFactory:
com.soywiz.korag.webgl.AGFactoryWebgl # <target=js>
com.soywiz.korag.awt.AGFactoryAwt # <target=jvm>
This allows me to just use one library so I do not need to replicate each library per target.
This part could be solved by having an interface and an implementation in a single place per library and then link different libraries per traget. But I do not feel it that robust. So one whishlist would be something like a ServiceLoader compatible with all kotlin targets and resolved at compile-time. If there is a mechanism for this in kotlin already, I would like to know it.
Dependency Injection
This is one of the reasons I would like to have reflection. I need to know constructors and the parameter types of a function/constructor in order to be able to do this properly. An opt-in full reflection would be really appreciated for this. For this a meta-programming processor would fit. But that would probably totally break any kind of incremental-compilation, at least without problematic bugs. I already experienced that with D and Haxe. So I prefer runtime reflection.
One example would be this. I’m requesting to the asynchronous injector SelectLevelScene, it detects the main constructor and requests other dependencies from there, also injecting annotations and using custom factories when annotated.
- https://github.com/soywiz/korge-samples/blob/develop/korge-simon/src/com/soywiz/korge/samples/simon/Simon.kt
- http://samples.korge.soywiz.com/simon/
class SelectLevelScene(
@Path("kotlin.atlas") val atlas: Atlas,
val ui: UIFactory
) : Scene() {
suspend override fun sceneInit(sceneView: Container) {
sceneView += ui.koruiFrame {
horizontal {
padding = Padding(0.2.em)
button("EASY").click { sceneContainer.pushTo<IngameScene>(Difficulty.EASY) }
button("MEDIUM").click { sceneContainer.pushTo<IngameScene>(Difficulty.MEDIUM) }
button("HARD").click { sceneContainer.pushTo<IngameScene>(Difficulty.HARD) }
}
}
}
}
De/Serialization
I’m using reflection for serialization and deserialization. I’m building classes from jsons/xmls/yamls. I know that you can use untyped objects like if they were real objects in JS, but they don’t have the object hierarchy so instance methods won’t work (even when you can create ), and I need mechanisms that works everywhere without changing code and that builds proper objects. So again full reflection would be awesome and won’t require to create custom compile-time code for each need I have.
Using the same source root for JS/JVM/Native code
I know of people that is creating symbolic links in order to be able to compile common data classes for JVM and JS. What I would like to have is to create a JAR with .class and code that just works on JS/Native too. But a mechanism that allows me to use the same source root with gradle/intellij for both and generate two libraries would be enough too.
Tree Shaking
This is an optimization and not required for me to start working on pure kotlin targets, but highly desirable for client-side code. With jtransc I’m doing treeshaking. That means that starting by the main method + service loaders + manually linked code I’m discovering other methods/classes/annotations + main constructors when classes are referenced and just including them in the final output. Similar like dart does. Or proguard, but without requiring rules and using annotations in code to define rules for very specific cases.
So this is my feedback/questions about this. What’s the status of those points? It is already possible to make my libraries compatible with kotlin.js? If not, there is something I can do to help? If there are already guideliness about how is Kotlin to face this, but it is a problem related to lack of time, I can help implementing optional full reflection in kotlin.js or things like that.
Thanks for your time!