Normally if you create a Stream
object, the head will be eagerly evaluated:
scala> Stream( {println("evaluating 1"); 1} , 2, 3)
evaluating 1
res63: scala.collection.immutable.Stream[Int] = Stream(1, ?)
If we create a Stream to which we prepend in the same statement, it seems slightly surprising that the head is not evaluated prior to the concatenation. i.e.
scala> 0 #:: Stream( {println("evaluating 1"); 1} , 2, 3)
res65: scala.collection.immutable.Stream[Int] = Stream(0, ?)
(#::
is right-associative and is the prepend method on ConsWrapper
, which is an implicit class of Stream
.)
How does this not evaluate its head before prepending the 0? Is it that the tail Stream (or cons cell) does not exist on the heap until we take values from the resultant Stream? But if so, how do we call the #::
method on an object that doesn't exist yet?
-Xprint:typer
is your friend, any time you want to understand exactly how some code is evaluated or types are inferred.
scala -Xprint:typer -e '0 #:: Stream( {println("evaluating 1"); 1} , 2, 3)'
val x$1: Int = 0;
Stream.consWrapper[Int](Stream.apply[Int]({
println("evaluating 1");
1
}, 2, 3)).#::(x$1)
The parameter of consWrapper
is by-name. So even this works:
scala> (1 #:: (sys.error("!!"): Stream[Int])).head
res1: Int = 1
The head is evaluated in the moment the Stream is created.
But in you second example you don't pass a Streem as the second argument to #::
you pass a by name parameter, i.e. the complete expression Stream( {println("evaluating 1"); 1} , 2, 3)
isn't evaluated at all.
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