So I'm fooling around with sleepsort to try how I ...
# coroutines
r
So I'm fooling around with sleepsort to try how I can make channels and sequences interact:
Copy code
suspend fun main(args: Array<String>) = runBlocking {

    val unsorted = generateSequence { Random.nextInt() }.take(200)
    val sortedChannel = Channel<Int>()
    val sorted = sequence { for (msg in sortedChannel) yield(msg) }
    unsorted.map { async { delay(it * 10L); sortedChannel.send(it) } }
    sorted.forEach(::println)
}
But I'm getting an error about restricted suspending functions where I'm iterating over the channel. Can anyone tell me what that's about?
z
You're calling
map
on a sequence, not a list. If you look at the implementations of the map operator map, you'll see it's an inline function - that's why it works from suspend functions, because the code is effectively just copied into the call site. List.map can be inline (as well as it's map function) because it executes immediately (as soon as you run map it gives you a copy of the list with the mapped elements). Sequences are lazy though. So when you call map on a sequence, it doesn't execute anything immediately. It stores the map function so that when you need to iterate over the sequence later, it can call it then. Because it's storing the map function, it can't be inlined, so it's just storing the actual function, which isn't marked as suspendable.
r
Thought about that, but the Lambda passed to
sequence
is actually marked as suspend
z
The lambda passed to
map
isn't though.
What you're looking for is some kind of stream primitive that would let you run lazy operations. This doesn't exist yet in the standard library but will be getting worked on after the 1.0 release: https://github.com/Kotlin/kotlinx.coroutines/issues/254
r
Might be, but the
map
is compiling, it's the line before that I'm wondering about
And yeah I get why this might not work, but I'm specifically curious about what on the side of the language is causing this error, because I've never seen it before, and from what I have done so far with suspend funs this should compile
z
Oh, my bad. The issue you're running into there is that the sequence builder has the restricted suspension annotation so you can't call suspend functions that aren't defined on the scope.
r
Oh, I didn't know there's such a thing! That makes sense, I guess. Could make the error in this case a little more helpful though 😬 Thanks for pointing this out!
z
That steam abstraction is still what you actually need for what you're trying to do.
r
Yeah I got that, I thought I could hack my way around that, but I guess not 😄
z
You could just do
sortedChannel.toList()
but that would wait for all the items to get emitted, so probably not what you’re looking for
r
Actually that's what I ended up using now - I had a few other bugs in the logic so it wouldn't have worked like I posted it.
This is the working code now:
Copy code
suspend fun main(args: Array<String>) = runBlocking {

    val unsorted = List(50) { Random.nextInt(100) }
    val sortedChannel = Channel<Int>()
    launch {
        unsorted
            .map { launch { delay(it * 10L); sortedChannel.send(it) } }
            .forEach { it.join() }
        sortedChannel.close()
    }
    println(sortedChannel.toList())
}
z
nice