Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it impossible to use method references to `suspend` functions in Kotlin?

I have a list of Job instances which I want to cancel at some point after launch. This looks as follows:

val jobs = arrayListOf<Job>()
//launch and add jobs...
jobs.forEach { it.cancelAndJoin() } // cancels the jobs and waits for completion

Unfortunately, it's not possible to use a method reference here. The reason: cancelAndJoin is a suspend function, as the compiler complains:

jobs.forEach (Job::cancelAndJoin) 

"Error:(30, 24) Kotlin: Unsupported [Callable references to suspend functions]"

Why doesn't this work?

like image 994
s1m0nw1 Avatar asked Nov 10 '17 20:11

s1m0nw1


People also ask

How suspend functions work Kotlin?

A suspending function is simply a function that can be paused and resumed at a later time. They can execute a long running operation and wait for it to complete without blocking. The syntax of a suspending function is similar to that of a regular function except for the addition of the suspend keyword.

What is the difference between suspending and blocking in Kotlin?

BLOCKING: Function A has to be completed before Function B continues. The thread is locked for Function A to complete its execution. SUSPENDING: Function A, while has started, could be suspended, and let Function B execute, then only resume later. The thread is not locked by Function A.

How do you wait for Suspend to finish Kotlin?

To wait for a coroutine to finish, you can call Job. join . join is a suspending function, meaning that the coroutine calling it will be suspended until it is told to resume. At the point of suspension, the executing thread is released to any other available coroutines (that are sharing that thread or thread pool).

What is suspend modifier in Kotlin?

The Kotlin compiler transforms every suspend function to be a state machine, which optimises using callbacks every time a function needs to suspend. Knowing what the compiler does under the hood now, you can better understand why a suspend function won't return until all the work that it started has completed.


2 Answers

UPD: This has already been implemented in Kotlin 1.3.x. Taking a callable reference to a suspending function gives you an instance of KSuspendFunctionN (N = 0, 1, ...). This type has its invoke operator defined as a suspending function, so that you can invoke such a callable reference suspending a coroutine in the same way as a direct invocation would.


Basically, supporting this requires an additional portion of language design and does not simply come bundled with coroutines.

Why is it non-trivial? Because when you take a callable reference of an ordinary function e.g. String::reversed, you get something like a KFunction1<String, String>. If you could do the same with a suspend function, what would you expect to get?

If it's the same KFunctionN<...>, then there's an obvious problem that you can pass it around where an ordinary function is expected and call it, violating the rule that suspend functions can only be called inside coroutines (where the compiler transforms their call sites).

So, it should be something more specific. (I'm currently only speculating, without any idea of actual design attempts) It could be, for example, a SuspendKFunctionN<...>, with its invoke(...) being a suspending function, or it could (less likely) be a special notation only for passing a function reference where a suspend (T) -> R is expected, but anyway, a feature like this requires thorough design to be future-proof.

like image 105
hotkey Avatar answered Oct 20 '22 19:10

hotkey


These helpers currently lack in Kotlin Standard library, but you can implement your own.

For example:

suspend fun <T> Iterable<T>.forEachAsync(action: suspend (T) -> Unit): Unit {
    val list = this.map { e ->
        async(...) {
            action(e)
        }
    }
    list.forEach { it.await() }
}

However, what context to pass to async now depends on the threading model your service is using (i.e. do you want to do multi-threading or want to keep everything in a single thread).

like image 2
Nikola Mihajlović Avatar answered Oct 20 '22 20:10

Nikola Mihajlović