Reputation: 3260
I don't see a very clear definition of the yield
function in Kotlin.
Example in the link above doesn't mention much but the following,
val sequence = sequence {
val start = 0
// yielding a single value
yield(start)
// yielding an iterable
yieldAll(1..5 step 2)
// yielding an infinite sequence
yieldAll(generateSequence(8) { it * 3 })
}
println(sequence.take(7).toList()) // [0, 1, 3, 5, 8, 24, 72]
But the above example doesn't point out the significance of yield.
Upvotes: 50
Views: 17056
Reputation: 17721
You can think of yield()
as "return, and next time start from where you stopped":
val sequence = sequence {
val start = 0
yield(start) // first return
yieldAll(1..5 step 2) // if called again, will start from here
yieldAll(generateSequence(8) { it * 3 }) // if called more that six times, start from here
}
It creates state machine and stuff, but you can translate it to something like following in Java:
class Seq {
private AtomicInteger count = new AtomicInteger(0);
private int state = 0;
public int nextValue() {
if (count.get() == 0) {
return state;
}
else if (count.get() >= 1 && count.get() <= 5) {
state += 2;
return state;
}
else {
state *= 3;
return state;
}
}
}
In Java class we maintain explicit state by having two variables: count
and state
. Combination of sequence
and yield
allow this state to be maintained implicitly.
Note that yield()
is a suspending function, so it may be invoked only from another suspend
function or from within a coroutine.
Upvotes: 51
Reputation: 4992
Let's consider an example, where you are able to generate the next element of the sequence, but you do not see an easy way to implement a Java iterator.
fun fibonacci() = sequence {
var a_0 = 1
var a_1 = 1
// this sequence is infinite
while(true) {
val a_n = a_0 + a_1
a_0 = a_1
a_1 = a_n
//this is a suspend function call
yield(a_0)
}
}
The example uses the yield
function to return the next element of the sequence. The function is an example of a suspend
function in Kotlin. The call to the function suspends the execution of the sequence{..}
block, so the call stack is free.
Let's say we do the following
fibonacci().take(10).forEach{
println(it)
}
Every iteration of the forEach
loop will resume the sequence{..}
block from the previous yield
function call and let it run to the next yield
function call. The execution flow will mix the forEach
loop iterations with the sequence{..}
block evaluation. You may try writing the same as Java Iterator
to feel what Kotlin compiler does behind the scenes.
suspend
functions in Kotlin are done minimalistic on the language and the standard library side, the rest can be implemented in libraries. I recommend checking the kotlinx.coroutines
library for more insides, examples, and documentation
https://github.com/Kotlin/kotlinx.coroutines
Upvotes: 7