Alexey Nezhdanov
Alexey Nezhdanov

Reputation: 127

kotlin coroutines: possible without standard library?

My question is rather theoretical. I am quite new to kotlin (only passed the tutorial, didn't write any real code).

Reading through the language reference I find myself confused about the fact that "suspend" is a keyword, yet I can't find anything like "launch" in the list of keywords. That makes me think that there is some asymmetry - the "suspend" is a compiler feature, yet "launch" is a library function. Is my understanding correct? If so - wouldn't it have been better to implement both as library features or both as compiler features?

I always thought that you can always write your own standard library using the available language features, but I still can't see if this really applies to this case.

TL;DR: Can I start a coroutine using pure kotlin, without importing any libraries whatsoever (however ugly that would be)?

Upvotes: 8

Views: 1632

Answers (5)

West_JR
West_JR

Reputation: 444

@Alexey Soshin's isn't quite correct.

You can use coroutines w/o the library, and it's pretty easy. Here is a about the simplest suspending coroutine example that has 0 dependency on the coroutine library.

import kotlin.coroutines.*

fun main() {

    lateinit var context: Continuation<Unit>
    
        suspend {
            val extra="extra"
            println("before suspend $extra")
            suspendCoroutine<Unit> { context = it }
            println("after suspend $extra")
        }.startCoroutine(
            object : Continuation<Unit> {
                override val context: CoroutineContext = EmptyCoroutineContext
                // called when a coroutine ends. do nothing.
                override fun resumeWith(result: Result<Unit>) {
                    result.onFailure { ex : Throwable -> throw ex }
                }
            }
        )
    
        println("kick it")
        context.resume(Unit)
}

This runs fine on the play.kotlinlang.org site.

As you can see from this code, any lambda decorated with suspend has the startCourtine() on it.

In fact, I think the SequenceBuilder() from the standard collection classes uses a simple coroutine like this to generate the sequence, with no dependency on the coroutine library.

The compiler is doing the heavy lifting on the coroutines, splitting the code into different "methods" at each possible suspending point. Look at the java code for this, and you'll see it's "split" into a switch statement. one case before the suspend, and another after.

The library does a ton of nice stuff for you..... and it's likely you'll almost always use it (cuz why not?) but you don't actually need it.

Upvotes: 4

Alexey Nezhdanov
Alexey Nezhdanov

Reputation: 127

Answering my own question here.

After a year of Kotlin I tend to think that this IS indeed possible. The suspend language feature creates an extra class and instantiates it every time your suspend function is called. This class extends ContinuationImpl and stores the progress of your coroutine - to which point it was able to execute so far.

Therefore one will need to write a custom dispatcher that would be able to manage the queue of the continuation objects to decide which one has to run now and a launch function that would take the newly created continuation object and pass it over to the dispatcher.

Now, this is still an asymmetry - the ContinuationImpl lives in kotlin.coroutines.jvm.internal so the compiler assumes this package exists. If one really wants to drop the standard library altogether - he'll need to implement that package to be able use the suspend keyword.

I'm not a kotlin expert though, so I might be wrong.

Upvotes: 2

Alexey Soshin
Alexey Soshin

Reputation: 17721

Can I start a coroutine using pure kotlin, without importing any libraries whatsoever (however ugly that would be)?

No. All coroutine generators are inside kotlinx.coroutines library, so you'll need at least that. Now, very theoretically, you could reimplement this functionality yourself. But probably you shouldn't.

How this can be done is a bit too long for a StackOverflow answer, but try invoking method of this Kotlin class from Java:

class AsyncWorks {
    suspend fun print() {
        println("Hello")
    }
}

You'll see that although Kotlin method has no arguments, in Java it requires Continuation<? super Unit>. This is what suspend keyword does. It adds Continuation<T> as the last argument of our function.

wouldn't it have been better to implement both as library features or both as compiler features?

Ideally, you'd want everything to be a "library feature", since it's easier to evolve. Removing a keyword from a language is very hard. In theory, having suspend as a keyword could be avoided. Quasar, being a framework, uses annotations instead. Go programming language, on the other hand, assumes all functions are suspendable. All those approaches have their advantages and disadvantages.
Kotlin decided to be pragmatic, and add suspend keyword, leaving the decision on the developers. If you're interested in the topic, I highly recommend this talk by Roman Elizarov, author of Kotlin coroutines, that explains their decissions: https://www.youtube.com/watch?v=Mj5P47F6nJg

Upvotes: 0

Marko Topolnik
Marko Topolnik

Reputation: 200206

The suspend marker adds a hidden continuation parameter to the function signature and completely changes the implementation bytecode. Suspension points don't boil down to helper function calls, they turn your linear program code into a state machine, the state being kept in the continuation object. The resulting bytecode isn't even representable as Java program code.

As opposed to that, launch is just regular library code that builds upon the suspend/resume primitive.

Upvotes: 4

Louis Wasserman
Louis Wasserman

Reputation: 198311

Because coroutines are valid for use cases that don't support launch. Because suspend requires some specific support from the compiler and launch doesn't if you already have suspend. Because structured concurrency is a library framework on top of the language feature, and launch is a part of that specific framework, that makes specific choices on top of what the language requires.

Starting a coroutine without any libraries can be done with startCoroutine. kotlin.coroutines is part of Kotlin, not a library.

Upvotes: 1

Related Questions