Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When exactly is the head of a Stream evaluated?

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?

like image 364
Luigi Plinge Avatar asked Mar 25 '12 01:03

Luigi Plinge


2 Answers

-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
like image 128
retronym Avatar answered Nov 18 '22 12:11

retronym


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.

like image 25
Jens Schauder Avatar answered Nov 18 '22 12:11

Jens Schauder