Question

Understanding Kotlin's yield function

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.

  • What does it mean that it is a suspending function?
  • In what scenarios can it be advantageous?
 48  16418  48
1 Jan 1970

Solution

 49

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.

2019-03-12

Solution

 7

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

2019-03-12