Question

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?

Was it helpful?

Solution

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top