question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Problems with flatMap function on ReceiveChannel<T>

See original GitHub issue

The ReceiveChannel<E>.flatMap function currently works like this:

  1. take the 1st ReceiveChannel<R> from transform function and pipe all values from it to the flatMap result channel,
  2. take the 2nd ReceiveChannel<R> from transform function and pipe all values from it to the flatMap result channel, 3 … n …

This way the following test:

val channel = produce {
    send("1000")
    send("1")
}

val list = channel.flatMap { value ->
    produce {
        delay(value.toLong())
        send(value)
    }
}.toList()

assertEquals(listOf("1", "1000"), list)

fails. That is if a value from the 2nd ReceiveChannel<R> comes earlier than the value from the 1st ReceiveChannel<R> it still is processed after the value from the 1st ReceiveChannel<R>.

I think it should work on a first-come-first-go basis. A preliminary version of my version of flatMap is sth like this:

private fun <E, R> ReceiveChannel<E>.flatMap(context: CoroutineContext = Unconfined,
                                          transform: suspend (E) -> ReceiveChannel<R>): ReceiveChannel<R> =
    produce(context) {
        val transformed = mutableListOf<ReceiveChannel<R>>()
        val deferredList = mutableListOf<Deferred<ReceiveChannel<R>>>()
        var finishedSource = false

        while (isActive &&
                (!finishedSource || transformed.isNotEmpty() || deferredList.isNotEmpty())) {
            selectUnbiased<Unit> {
                this@flatMap.onReceiveOrNull { value ->
                    when {
                        value != null -> deferredList += async(context) { transform(value) }
                        else          -> finishedSource = true
                    }
                }

                transformed.forEach { output ->
                    output.onReceiveOrNull { value ->
                        when {
                            value != null -> this@produce.send(value!!)
                            else          -> transformed -= output
                        }
                    }
                }

                deferredList.forEach { deferred ->
                    deferred.onAwait { output ->
                        deferredList -= deferred
                        transformed += output
                    }
                }
            }
        }
    }

It might not work correctly all the times (e.g. I haven’t done any analysis on cancelling behaviour), but it should give you an idea of how I imagine it.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:6 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
elizarovcommented, Dec 5, 2017

Thanks. Indeed, a sequential flatMap is not that useful for asynchronous channels. My suggestion to solve this problem is this:

  1. Reserve flatMap name for synchronous cases, e.g. when transform returns either a Collection or a Sequence (implement the corresponding versions)
  2. Deprecate flatMap with ReceiveChannel transforms to avoid any confusion and consider reintroducing it under the name of concatMap. For the sake of consistency, concat operation on two channels should be introduced, too.
  3. Use different names for concurrent cases. We can use other Rx-inspired names for concurrent versions (I don’t see big benefit in reinventing new names here):
  • mergeMap that would process all items on first-come-first-served basis. The corresponding merge operator for two channels would also help.
  • switchMap that would take items from the first channel only only the next channel is received (switch to it), etc. The corresponding switch operator for two channels might also be helpful.
0reactions
qwwdfsadcommented, Dec 13, 2019

Closing as obsolete, flatMap on the channel is deprecated

Read more comments on GitHub >

github_iconTop Results From Across the Web

Array.prototype.flatMap() - JavaScript - MDN Web Docs
The flatMap() method returns a new array formed by applying a given callback function to each element of the array, and then flattening...
Read more >
Scala | flatMap Method - GeeksforGeeks
Here, flatMap is applied on the another function defined in the program and so a list of sequence of numbers is generated.
Read more >
Java 8 Streams FlatMap method example - Stack Overflow
The Function passed to flatMap is responsible for creating the Stream , so the example I give is really shorthand for integerListStream .flatMap(ints...
Read more >
PySpark flatMap() Transformation - Spark by {Examples}
PySpark flatMap() is a transformation operation that flattens the RDD/DataFrame (array/map DataFrame columns) after applying the function on every element.
Read more >
Project Reactor: map() vs flatMap() - Baeldung
Now, let's see how we can use the map operator. The Flux#map method expects a single Function parameter, which can be as simple...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found