I'm migrating some code from Scala to Kotlin, I observe different behaviour:
Scala:
var i = 0
Iterator.continually{
println(s"i=$i")
i += 1
i
}.takeWhile {
_ < 3
}.foreach { i =>
println(s"I=$i")
}
Output:
i=0
I=1
i=1
I=2
i=2
Kotlin equivalent:
fun <A> continousIterator(func: () -> A): Iterable<A> =
object: Iterable<A> {
override fun iterator() =
object: Iterator<A> {
override fun hasNext(): Boolean = true
override fun next(): A = func()
}
}
var i = 0;
fun main() {
continousIterator{
println("i=$i")
i += 1
i
}.takeWhile{
it < 3
}.forEach {
println("I=$it")
}
}
Output:
i=0
i=1
i=2
I=1
I=2
When we have state, the result isn't the same, the calling order of func()
and the iterator
are different.
I wonder why.
In Kotlin, when takeWhile
is called, takeWhile
iterates through the sequence immediately and prints 0 to 2, and produces a List
. Only after that does the forEach
get run, printing 1 and 2.
To replicate the Scala behaviour, you need forEach
to consume the sequence, instead of takeWhile
. A Sequence
can do this:
generateSequence {
println("i=$i")
i += 1
i
}.takeWhile{it < 3}.forEach { println("I=$it")}
Operations on a Sequence
are all lazy. They don't consume the sequence unless absolutely necessary.