I want to create a pure asynchronous GraphQL server library based on kotlin coroutines, but I hesitated because java will officially release its experimental feature loom/virtual thread.
Between kotlin coroutines and jvm virtual threads, which one will developers prefer?
Right now jvm virtual threads don’t really exist. So if you want to code something now, it’s probably better to stick with coroutines which have been released.
Once there is a stable reelase of jvm virtual threads, the answer to what’s better might change obviously.
Another things that might help you take a decision :
- Which server engine are you planning to use/bind on ? For example, Spring Webflux provides a lot of facilities to map coroutines and reactor. Ktor, of course, is based on coroutines. Other engines work directly with java multi-threading facilities. In such case it would be easier to stick with loom to improve compatibility and maintainance.
- Coroutine is the Kotlin public api to provide structured concurrency and asynchronicity. If your aim is to favor binding with Kotlin apps more than with Java apps, I think Coroutine would be a better choice. If you favor java users, maybe loom would be a better choice.
- Loom restricts you on the JVM (and often, that is the better choice), but coroutines will allow you to make a multi-platform project if you’d like to target native apps or NodeJS apps. Note however, I have not a lot of experience with multi-platform, so if you’re interested in that, you’ll have to search/ask for coroutine limitations on each platform.
As @al3c already said, however, Loom is not even shipped in the stable releases of the JDK yet, so you might stumble upon API instabilities if using Loom.
My personal opinion :
I would stick with coroutines.
Nothing will prevent kotlin coroutines to use loom internally in the long term (because on short terms, retro-compatibility or lack or information might prevent that) if it can provide performance improvement. However, in term of public tools/api, I think that coroutines will always be the “kotlin way” to do async code. For one, language provides the suspend keyword to ease async function interactions, and all coroutine library components are designed with kotlin philosophy in mind :
- be explicit, but remove useless boilerplate
- Use the best of both worlds : object and functional programming
- provides facilities through extensions
EDIT: I’ve almost the most important point in my opinion : null-safe APIs
I’m working exclusively with Java for ten years now. And with Loom, as with any other new tools I’ve seen shipped in the language this past few years, I expect Loom to be a very powerful tool, but with a complicated API, making it difficult to use and maintain.
If you’re going to make a library, then it’s important to consider where it will be able to be used.
If you write an async library with Kotlin coroutines, then only Kotlin programmers can use it.
If you write an async library using JVM virtual threads, then only Loom developers will be able to use it that way – and that’s nobody for quite some time. Java and Kotlin developers could use it with real threads instead of virtual threads, but of course that’s not going to perform the way your want.
If you write an async library using monadic-style programming in Java with
CompletableFuture, then all Java and Kotlin developers will be able to use it, and it will be easy to wrap it in a coroutine-based API in either Kotlin or Java/Loom. That sucks for you as the library author, but it’s how you write the most widely usable library.
Yes, I changed my goal, base on CompleteFuture, and use kotlinx-coroutine to convert Mono to complete future.
Now, I only want to support kotlin because
- The new library will be created base on GitHub - babyfish-ct/kimmer: Port "https://github.com/immerjs/immer" for kotlin/jvm
- The new library use the DSL style API, and kotlin can create DSL very easy
Thanks for so long relay
My original question has been resolve. I give up this short goal, and I will use reactor/kotlinx-coroutine-reactor to reach my long gobal.
But I have a new question.
- kotlin coroutine means suspending and resuming by implicit state machine
- virtual thread means resizable stack
Both of them are super cool, I don’t know which one is the most popular choice in future(eg, 10 years later)
In the long term future, if virtual threads as implemented by the JVM are better for performance than Coroutines (which is not sure at the moment), then the Kotlin team will rewrite Coroutines on the JVM to use these. It’ll probably take a few months at least, but you mentioned 10 years.
So by choosing Coroutines, you have in the long term:
• all benefits of Coroutines (on all platforms)
• on the JVM, also all the benefits of virtual threads, if there are any
But why do this? Why not expose Reactor primitives, they are much more powerful than CompletableFuture. If you expose Mono consumer can use the same adapters to work with them using Reactor, RxJava, Coroutines or CompletableFuture
Pseudo-sync style code is simple and readable, this is the most important point
virtual thread will become better, not only simple and readable, but also easy to debug
Yes, of course, but CompletableFuture is not pseudo-sync, right?
Oh, I get you point.
I want to give pseudo-sync style code for developer.
But graphql-java(My framework bases on it) only accept CompletableFuture, so I have to convert it.
Such a library already exists - see GraphQL Java Kickstart. It supports a reactive GraphQL server in Java via Spring WebFlux and in Kotlin via Coroutines. You can see an example of a coroutine-based GraphQL server here.
The “Code First” alternative for GraphQL Java Kickstart is ExpediaGroup GraphQL Kotlin.
And you can find more alternatives here.
Do you have any source for this claim, i.e., has there ever been an official statement claiming that retrofitting Kotlin coroutines to Loom’s virtual threads is being considered? Even though Loom comes with continuations as well, its notion of what a continuation actually is, which properties it has and at which point in time it is created is actually quite different.
This question is asked quite often. Multiple devs from the Kotlin team have answered that updating KotlinX.Coroutines so they use Project Loom under the hood was an option, but they were waiting to see an actual Project Loom implementation before making a decision.
I don’t have a link to that at the moment, but if you search for similar questions on this forum + on Slack, you should find these comments at some point.