Marcel (1) [Avatar] Offline
#1
Hi all,

I have tried to find if this subject was already handled, but I couldn't find it. Hope I am not duplicating here.

I am confused about the apply method on page 68 of the book in the object Stream. The signature:

def apply[A](as: A*): Stream[A]

This method was added as a convenience method for easy construction. Problem is, as far as I understand, that it evaluates the thunks immediately (before passing it to cons). When constructing something like this 1 2 and 3 are printed before 'not printed first...':

    val stream = Stream({println("1");1}, {println("2");2}, {println("3");3})
    println("not printed first...")
    println("to list: "+stream.toList)


This is because a Seq[A] impl is created which evaluates the thunks before creating a stream with it.

However, not using the apply method works fine:

    val stream =
      Stream.cons({println("1");1},
        Stream.cons({println("2");2},
          Stream.cons({println("3");3},
            Stream.empty)))
    println("printed first")
    println("to list: "+stream.toList)


Do I make a mistake here or is the apply method not a good option for creating a stream?

Thanks,
Marcel
lewistg (2) [Avatar] Offline
#2
I am not sure of the exact answer, but I can see your point. Even Scala's standard Stream implementation seems to be strict in its arguments (plugging your example into the REPL has the same result). I suppose it's just a convenient way of creating a simple Stream built from expressions you know do not "evaluate to bottom" (e.g., Stream(willNotLoopForever1(), willNotLoopForever2(), willNotLoopForever3()).

You could always make a method like this:
def lazyApply[A](as: (() => A)*): Stream[A] = if (as.isEmpty) empty else cons(as.head(), lazyApply(as.tail: _*))

But then it is pretty clunky to create a list:
val stream = Stream.lazyApply[Int](() => {println("1");1}, () => {println("2");2}, () => {println("3");3})


Maybe the Stream.apply method exists as a way to build more complex Streams from list-like Streams (where you can enumerate all the elements beforehand) and infinite Streams (where you can't enumerate everything beforehand). Hopefully, someone can shed some more light on this. These are just some ideas.
377486 (1) [Avatar] Offline
#3
Just ran into this myself. I think the reason must be that repeated by-name parameters are not currently supported in Scala (see http://docs.scala-lang.org/sips/pending/repeated-byname.html). Consequently all arguments to Stream.apply are evaluated even before the function is called, and you'll end up with a fully evaluated Stream. (This is so even in the standard library).

So Stream.apply in its current form is indeed not terribly useful.