Multitargeting requires actual stdlib, not just wrappers


#1

I’m trying to reuse some validation code on js frontend and current 1.1-beta38 makes it really hard.
It’s missing such crucial things as all the Math.*, String.valueOf(int/double), any regex api forcing to abstract them and provide separate jvm and js implementations if one wants to compile code on both targets.
Providing kotlin.js.Math is ok, but i guess kotlin needs actual kotlin.Math for multitargeted code.
This splits kotlin to two incompatible dialects being that this apis are so fundamental.
Whats the point of being able to compile to jvm and js if user required to provide implementations (actually bad implementaions since there is no inlining for simple functions) for string/regex/math api?
I would consider solution for this problem a critical priority task since js is now officially supported platform.
Guess the best one would be GWT style Java API emultaion since everyone already knows it, but looking at changes it seems like target is new kotlin stdlib api.

/rant


#2

What approach do you use to compile your code for both targets?

We’re aware of problems with Math, but not with string/regex. Could you share some examples what specifically is inconvenient, and what implementations you have to provide in JS?


#3

I’m using kotlin2js gradle plugin with main.kotlin.srcDirs +=“other kotlin module sources path” to compile shared code to js

There is no problems if code is meant for jvm or js individually. But current api’s makes it hard to crosscompile same source code for both targets. api present in kotlin-stdlib-js is missing in jvm and vice versa. There is no single regex class, no single Math class. In js String has no implementation for toInt/toDouble etc. Some of these are really basic things present even in C stdlib and missing in kotlin. I know i can implement wrappers to use Java api for server side and use js globals from kotlin-js on client side, but there is no point for js stdlib then. I can write wrappers for anything with js("") blocks. If i have to write such low level api wrappers kotlin becomes just another syntax for js with weak interop and dto only code sharing.

I would expect to be able to share most kotlin code on client/server side which does not uses any java specific frameworks/features like threads. Actually current state is already really not bad supporting lot of usefull things. But this Math update is really bad one for code portability. No one expects for sin/cos to break. String.toInt/toDouble is another big one.

Also kotlin stdlib js has small utility methods and extension functions and even jquery apis which pollutes standard api. This is a problem since DOM and other js api’s are currently fast moving targets. Their current implementation targets older browsers and is suboptimal and actually contributes to size of kotlin.js module. This is just both wrong and inconvenient if you provide API wrappers and utilities make it explicit at least. It is even more surprising that js is moving to hard modularization but kotlin being statically typechecked has almost global monkeypatching :slight_smile:

I guess it is too early to talk about minimization and js optimizations but GWT basically murders kotlin in produced js size(and no uglify/closure compiler does not help much). Btw produced js size was primary reason of Dart initial fiasco. Every troll on every programming forum mocked 1MB hello worlds. Kotlin js has 1.2MB hello worlds. :slight_smile:

Ok, i think this is already big enough of a rant. This problems btw surfaced after only minor porting of dto and validation code. I currently have not tried to port math heavy parts and complex things like caches.

For reference of actual usable api surface for crosscompilation to js is GWT, over the years it has implemented meaningful subset of Java api (https://github.com/gwtproject/gwt/tree/3a41a8067767965ff72f4e49eb015f587fcfee16/user/super/com/google/gwt/emul/java)
It is somewhat bloated but makes porting non framework specific java code with no threads/files is really simple.
It still have some raw edges though like working with Date formatting(with really ugly hacks like rendering dates in different timezone and splitting generated strings). But basic stuff like Strings, Lists, Maps, Math, simple parsing all are really solid.


#4

ok, i just tested beta38 it has String.toInt implemented. So +1 point to stdlib :slight_smile:


#5

Basically, the desire to write crosscompiled code is very understandable, and so is the demand to have a rich standard library with the unified API across different target platforms. We have plans to improve multitargeted project development experience with the language features, tooling support and of course a subset of the standard library, which is available in all target platforms. Some of these improvements will be released in one of Kotlin 1.1.x updates in the experimental status.

However the rich standard library may end in being bloated with the unnecessary code, and that is what we’d like to avoid. So we’re not going to blindly copy GWT’s approach.

What we’re interested in at the current phase of the development is your feedback about what do you miss in particular in the common subset of the standard library, and the use cases explaining why.

Have you tried kotlin.text.Regex?

This is no longer a problem since the second beta of 1.1, as you have already noticed.

That is unfortunate, however there is a workaround, see KT-16175.

JQuery API is deprecated and is going to be eventually removed from the standard library as soon as we cut it out to a separate library artifact.
What are the others? Could you provide some examples and explain why they are suboptimal?


#6

i could not make this work. see my comment there


#7

I somehow missed it. oops :rolling_eyes:

It is already somewhat bloated.

I guess all DOM/js api helpers. JS already passed this once and it was considered bad idea to extend any standard api. Although main reason named was monkeypathcing conflicts which does not affect kotlin code.

Problem is JS not being single target has IE, Chrome, Firefox, Node, Safari implementations. kotlin-js includes some random org.w3c.* api s. For example you provide fetch api in stdlib-js, but do you provide actual polyfill (no. but somehow still eats ~200 lines in kotlin.js)? Is it usable from other js modules (no)? Actually it is hard to test since chrome/firefox already shipping it. But iOS will be broken. This means one cannot be sure if api is present even if it is provided in kotlin-stdlib-js and call to declared method could result in “fetch is undefined”, or on IE very informative “error error” :slight_smile:
Btw having separate modules like kotlin-js-dom2-ie-api seems logical but will not work. See detect browser features vs versions internet debates from decade ago.
Also kotlin typesystem is not very flexible and in general it is hard to express js api in kotlin. Try doing it with something like _.debounce

GWT way of solving of all this problems was implementing wrapping everything and providing full platform. There is com.google.gwt.user.client.Element, not org.w3c.dom.Element. Also swing like widgets library, custom centralized event system. It worked very well in 2005 for IE5.5. Not so much in 2017. Compared to React/Vue/Angular this approach is just too much work for less result.
Typescript solved this by not being crossplatform and targetting JS only.

There where lot of attempts to this kind of problem. They all end up in two ways. Java way - provide paltform. C way - provide tools to make platform specific code bearable. Looks like kotlin tries to avoid both :slight_smile:

Personally my crosstarget language of the dreams should have smallish stdlib to make DTO/algo code crossplatform and totally skip the rest. Kind of cpp but with GC and sane macro system. :smiley:

In general as I said before my kotlin js expirience is rather limited to moment and most problems I have encountered are from trying to reuse validators. Hope this helps.


#8

The way I look at it is that multitargeting should make it easy to have your own code be shared across platforms. That is business logic, types etc. There will still be large parts that are platform specific for JVM,JS or native. In the case of JavaScript, there are so many changes and so many platform specific aspects (and needs for polyfills) that it would make little sense to put it in the standard library. Remember that if something is in the standard library it is impossible to get rid of it (almost). Instead good secondary libraries should provide that functionality, and in many ways that is the role of the kotlinx.* libraries.


#9

It would be great if there would be clear APIs for each back-end:

  • All: A very basic API that JetBrains is willing to commit to for all back-ends.
  • Java, superset of all: The API available on the JVM back-end.
  • JavaScript, superset of all: The API available on the JavaScript back-end.

If you want to share a module across back-ends, you can only use all.

This is of course a very simplistic example. I can image that there may be other API levels that can be shared between a number of back-ends.


#10

Providing platform API’s in kotlin is too much of responsibility. It will be “Almost always runs everywhere” situation Java currently has but much worse due to kotlin being younger and having less resources.

After some time thinking about multitargeting providing C style macro, low level hack tools and minimalistic stdlib for portability of common code seems like the only option kotlin team has.

If kotling teams wants to target js, llvm, jvm, android same time platform API surface will be just too huge. Having direct acces to Java apis is helpfull but is useless for both js and llvm. llvm target will be of any use to anyone only if kotlin will be able to integrate somehow with C libs(while still having GC which is really non trivial). And current js target is forcing some tricky things to make stuff work. Without dynamic and js("") it will be plain painfull.