I have a deeply "nested" for-comprehension, simplified to 3 levels below: x, y, and z. I was hoping making only x a Stream
would make the y and z computations lazy too:
val stream = for {
x <- List(1, 2, 3).toStream
y <- List("foo", "bar", "baz")
z = {
println("Processed " + x + y)
x + y
}
} yield z
stream take (2) foreach (doSomething)
But this computes all 3 elements, as evidenced by the 3 prints. I'd like to only compute the first 2, since those are all I take from the stream. I can work around this by calling toStream
on the second List
and so on. Is there a better way than calling that at every level of the for-comprehension?
What it prints is:
Processed 1foo
Processed 1bar
Processed 1baz
stream: scala.collection.immutable.Stream[String] = Stream(1foo, ?)
scala> stream take (2) foreach (println)
1foo
1bar
The head of a Stream
is always strictly evaluated, which is why you see Processed 1foo
etc and not Processed 2foo
etc. This is printed when you create the Stream, or more precisely, when the head of stream
is evaluated.
You are correct that if you only wish to process each resulting element one by one then all the generators will have to be Streams. You could get around calling toStream
by making them Streams to start with as in example below.
stream
is a Stream[String]
and its head needs to be evaluated. If you don't want to calculate a value eagerly, you could either prepend a dummy value, or better, make your value stream
lazy
:
lazy val stream = for {
x <- Stream(1, 2, 3)
y <- Stream("foo", "bar", "baz")
z = { println("Processed " + x + y); x + y }
} yield z
This does not do any "processing" until you take each value:
scala> stream take 2 foreach println
Processed 1foo
1foo
Processed 1bar
1bar