Kotlinx.coroutines: Deferred has no map function

Created on 21 Apr 2018  路  5Comments  路  Source: Kotlin/kotlinx.coroutines

there should be a function

Deferred<R>.map(map:(R)->T) : Deferred<T>

it is easy to implement yourself, but it is a bit confusing that it is missing.
there might be other functions to consider, like

Deferred<Deferred<T>>.flatten() : Deferred<T>

or

List<Deferred<T>>.combine() : Deferred<List<T>>
wontfix

Most helpful comment

Le me add a note explaining why suspending function are preferable to functions returning Futures (incl. Deferred, Job, and others):

Marking a function suspend in Kotlin is semantically the same as making the function returning a future in Java. A suspending function IS a callback function. The only difference, is that the callback mechanism is hidden by the compiler, and we don't actually see the callback, which make the code clearer. But it is a callback.

So, the whole point of Kotlin coroutines is to not use callback and future anymore. Instead we use suspending functions which lets us write code like sequential code while keeping the benefits of future and callbacks.

Therefore declaring a function fun foo(): Deffered<Int> instead of suspend fun foo(): Int is basically loosing the whole point of Kotlin coroutines. This could be done with any future library.

Consider this Java code:

public CompletableFuture<Integer> foo() { ... }
public CompletableFuture<Integer> bar(int arg) { ... }
public CompletableFuture<Object> usage() {
    return foo().thenCompose(x -> bar(x)).thenApply(it -> it + 1);
}

In this code we have to use Future, combine, map etc. or, because there is no better choice.

But the point of kotlin coroutines is to provide the suspend keyword in the language which hide all the complexity.

So here is the Kotlin idiomatic equivalent of the Java code above:

suspend fun foo(): Int { ... }
suspend fun bar(arg: Int) { ... }
suspend fun usage() = bar(foo()) + 1

All 5 comments

The coroutines philosophy is to design the software architecture around suspend functions and avoid creating function returning Job or Deferred.

If you follow this philosophy, you'll never need to map a Deferred as the Deferred will be created at the most top level and used with await.

Example:

// don't do:
fun foo(): Deferred<Int> = TODO()
fun bar(arg: Int): Deferred<Int> = TODO()
val deferred = foo().flatMap { bar(it) }.map { it + 1 }

// do:
suspend fun foo(): Int = TODO()
suspend fun bar(arg: Int): Int = TODO()
val deferred = async { bar(foo()) + 1 }

I believe this is the reason why there is no promise-like operators in kotlinx.coroutines.

Note that one may not follow this philosophy and create Deferred.map as well as other operators (then, flatMap, etc.). But isn't this losing the benefit of coroutines? In this case what would be the point of coroutines over Futures/Promises?

Le me add a note explaining why suspending function are preferable to functions returning Futures (incl. Deferred, Job, and others):

Marking a function suspend in Kotlin is semantically the same as making the function returning a future in Java. A suspending function IS a callback function. The only difference, is that the callback mechanism is hidden by the compiler, and we don't actually see the callback, which make the code clearer. But it is a callback.

So, the whole point of Kotlin coroutines is to not use callback and future anymore. Instead we use suspending functions which lets us write code like sequential code while keeping the benefits of future and callbacks.

Therefore declaring a function fun foo(): Deffered<Int> instead of suspend fun foo(): Int is basically loosing the whole point of Kotlin coroutines. This could be done with any future library.

Consider this Java code:

public CompletableFuture<Integer> foo() { ... }
public CompletableFuture<Integer> bar(int arg) { ... }
public CompletableFuture<Object> usage() {
    return foo().thenCompose(x -> bar(x)).thenApply(it -> it + 1);
}

In this code we have to use Future, combine, map etc. or, because there is no better choice.

But the point of kotlin coroutines is to provide the suspend keyword in the language which hide all the complexity.

So here is the Kotlin idiomatic equivalent of the Java code above:

suspend fun foo(): Int { ... }
suspend fun bar(arg: Int) { ... }
suspend fun usage() = bar(foo()) + 1

Closing this issue. Thanks to @jcornaz for quite a thorough explanation.

@jcornaz @elizarov thanks.

Dart has a advanced Asynchrony support:
https://www.dartlang.org/guides/language/language-tour#asynchrony-support

Dart suggest Handling Futures,Handling Streams.
For instance, we could :

foo() async { ... }
bar(arg: Int) async { ... }
usage() async => (await bar((await foo()))) + 1

@SolomonSun2010 Thank. We've studied Dart design while working on Kotlin coroutines and we had put quite a lot of thought into making sure we don't fall into the same futures trap as Dart did. As a result, the Dart example you've given looks much simpler (and nicer) in Kotlin:

suspend fun foo(): Int { ... }
suspend fun bar(arg: Int) { ... }
suspend fun usage() = bar(foo())
Was this page helpful?
0 / 5 - 0 ratings