I know streams are supposed to be lazily evaluated sequences in Scala, but I think I am suffering from some sort of fundamental misunderstanding because they seem to be more eager than I would have expected.
In this example:
val initial = Stream(1)
lazy val bad = Stream(1/0)
println((initial ++ bad) take 1)
I get a java.lang.ArithmeticException
, which seems to be cause by zero division. I would expect that bad
would never get evaluated since I only asked for one element from the stream. What's wrong?
OK, so after commenting other answers, I figured I could as well turn my comments into a proper answer.
Streams are indeed lazy, and will only compute their elements on demand (and you can use #::
to construct a stream element by element, much like ::
for List
). By example, the following will not throw any exception:
(1/2) #:: (1/0) #:: Stream.empty
This is because when applying #::
, the tail is passed by name so as to not evaluate it eagerly, but only when needed (see ConsWrapper.# ::
, const.apply
and class Cons
in Stream.scala
for more details).
On the other hand, the head is passed by value, which means that it will always be eagerly evaluated, no matter what (as mentioned by Senthil). This means that doing the following will actually throw a ArithmeticException:
(1/0) #:: Stream.empty
It is a gotcha worth knowing about streams. However, this is not the issue you are facing.
In your case, the arithmetic exception happens before even instantiating a single Stream. When calling Stream.apply
in lazy val bad = Stream(1/0)
, the argument is eagerly executed because it is not declared as a by name parameter. Stream.apply
actually takes a vararg parameter, and those are necessarily passed by value.
And even if it was passed by name, the ArithmeticException
would be triggered shortly after, because as said earlier the head of a Stream is always early evaluated.
The fact that Streams are lazy doesn't change the fact that method arguments are evaluated eagerly.
Stream(1/0)
expands to Stream.apply(1/0)
. The semantics of the language require that the arguments are evaluated before the method is called (since the Stream.apply
method doesn't use call-by-name arguments), so it attempts to evaluate 1/0
to pass as the argument to the Stream.apply
method, which causes your ArithmeticException.
There are a few ways you can get this working though. Since you've already declared bad
as a lazy val
, the easiest is probably to use the also-lazy #:::
stream concatenation operator to avoid forcing evaluation:
val initial = Stream(1)
lazy val bad = Stream(1/0)
println((initial #::: bad) take 1)
// => Stream(1, ?)
The Stream will evaluate the head & remaining tail is evaluated lazily. In your example, both the streams are having only the head & hence giving an error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With