https://kotlinlang.org logo
#coroutines
Title
# coroutines
r

robin

10/16/2018, 3:01 PM
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

Zach Klippenstein (he/him) [MOD]

10/16/2018, 3:07 PM
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

robin

10/16/2018, 3:08 PM
Thought about that, but the Lambda passed to
sequence
is actually marked as suspend
z

Zach Klippenstein (he/him) [MOD]

10/16/2018, 3:09 PM
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

robin

10/16/2018, 3:10 PM
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

Zach Klippenstein (he/him) [MOD]

10/16/2018, 3:11 PM
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

robin

10/16/2018, 3:13 PM
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

Zach Klippenstein (he/him) [MOD]

10/16/2018, 3:13 PM
That steam abstraction is still what you actually need for what you're trying to do.
r

robin

10/16/2018, 3:15 PM
Yeah I got that, I thought I could hack my way around that, but I guess not 😄
z

Zach Klippenstein (he/him) [MOD]

10/16/2018, 3:33 PM
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

robin

10/16/2018, 3:37 PM
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

Zach Klippenstein (he/him) [MOD]

10/16/2018, 3:52 PM
nice
5 Views