Abstraction w/ JVM and JS impls


#1

Say, for example, I want a byte buffer that is backed by ByteBuffer on the JVM and Uint8Array in JS. Can this be done seamlessly without the caller knowing what environment he is in? I know how I would do this in ScalaJS. In my lib, I am trying to avoid having shared JVM/JS code all take a big Platform abstraction and have huge impls for JVM/JS and make the caller know about it.

I want the caller to not care and I want to choose a proper platform impl (even for native). It is not feasible for me to write my own in Kotlin wrapping a byte array because of efficiencies in the platform-specific versions. Any examples of this?


#2

This will soon be possible when we release our work on multiplatform projects.


#3

In the meantime you can use an interface and use a small factory to get the appropriate implementation. You’d have to write the onliner factory twice though (one for each platform) and do some small hackery to make it work.


#4

Yeah, that’s what I meant by “platform” and making the caller know. I want a shared library, and I don’t my lib to by like MyLibrary(platform: Platform) and have JvmPlatform and JsPlatform. I will wait for the feature…I am developing something and trying to keep the JVM and JS stdlib out so I can have reuse.


#5

What you can have is for each version of the library (the JS and JVM version) have a different implementation for the class that provides access to the concrete implementation for that platform. Basically you would have 3 source roots. One for the JVM, one for JS and one shared. You want to minimize the platform specific ones, but can put classes in there that have different implementations on both platforms.

Btw. There is one issue if you do this in Intellij. It assumes that every source file is only part of one source module (it doesn’t like sharing), symlinks are resolved so will not solve the problem, but automatic copying of shared code “works” as long as you take care to have intellij see it as generated (to help you not to edit it and have the edits overwritten).

Of course proper support would be better.


#6

I have successfully fooled around IDEA by creating actual folder and symlinking shared src sub folder there(please dont’ fix it JetBrains :smiley: ). Looks like IDEA actually does not like duplicate gradle files with same paths but same src is ok. Some manual editing of modules in project settings and .gitignore or analogues is required but still symlink is much better solution than copying src to build/generated and like.


#7

Symlinks have that drawback that they work in a single working copy. So in every other working copy one has to setup symlinks again.


#8

Setting up symlinks on linux/macos is trivially scriptable. You can do in gradle.build too.
But it will still be required to manually edit imported project paths in IDEA module settings. Maybe it is scriptable with gradle idea plugin. I have no idea how to do it :slight_smile:


#9

Has any further work happened around this? I see things like https://github.com/JetBrains/kotlin/tree/master/libraries/kotlin.test but I specifically need to emulate JVM classes. Will the approach instead require me to abstract out a common iface always? No prob if so. Also, what are those “impl”/“header” keywords? If all of this is still just experimental, is there an issue or branch or something I can watch for progress? I’ll be happy to give it a run through the paces if it’s in early beta/alpha.


#10

header is a function/class/interface without the body in a common module, and impl is the corresponding declaration with the body in a platform-specific module.
header is used to declare an API that is can be used in a common code, but have different implementations in different platforms.

Usually is this situation we declare a header class in a common module, implement that class in JS and provide impl typealias to JDK class in JVM. However it doesn’t work flawlessly now and may require suppressing some errors. And I’m not sure that trick with the impl typealias will be supported in the first release.

We commit all our work regarding multiplatform support to the master branch, so eventually it is delivered as an experimental functionality in 1.1.x releases. When it’s baked enough we’ll make an announcement, provide some tutorials, etc.

Current issues are tracked with the multiplatform tag.