I am completely new to Scala. I've been playing around with LazyLists. Consider the following:
val fun: Int => Int = (x: Int) => {
println("PROCESSING...")
x + 1
}
val lazyList = LazyList(fun(1), fun(2), fun(3))
The snippet above prints "PROCESSING..." thrice, which indicates that all three elements of LazyList were computed. I found such behaviour to be rather unexpected for a lazy collection. So, I decided to print it:
println(lazyList) // which prints "LazyList(<not computed>)".
I thought it would print out LazytList(2, 3, 4). (I'm not completely sure, but it seems to me that Scala's println works for lazy collections sort of like the :sprint command in GHCi, dividing the collection in two parts: the evaluated and the unevaluated one.)
So, here are my questions, concerning this code:
"PROCESSING..." thing about? If not, why does println claim so?LazyList's arguments like fun(1) to be computed right away? Why do we cast away the call-by-need strategy when initializing? Are there any other cases where such a thing happens? Note that no output is produced when we use map instead of writing this down manually, as expected.Instead of using LazyList.apply, any of the following work (without evaluating their arguments):
LazyList.tabulate(3)(fun)fun(1) #:: fun(2) #:: fun(3) #:: LazyList.emptyLazyList.range(1, 4).map(fun)
- Why do we want
LazyList's arguments likefun(1)to be computed right away? Why do we cast away thecall-by-needstrategy when initializing? Are there any other cases where such a thing happens? Note that no output is produced when we usemapinstead of writing this down manually, as expected.
I don't think it is desirable that fun(1) be computed right away, but it follows from the fact that you used LazyList.apply to construct your list. LazyList(fun(1), fun(2), fun(3)) is syntactic sugar for LazyList.apply(fun(1), fun(2), fun(3)) and the type signature for that function is def apply[A](elems: A*): LazyList[A]. Note that the arguments of that function are not call by name.
So: why is LazyList.apply not defined with call-by-name arguments?
def apply actually comes from SeqFactory which is mixed into most collection companion objects. The LazyList companion object might be the one place in the collections library where the companion object apply isn't a helpful addition.
- Why are no elements displayed as evaluated? If they are indeed unevaluated, what was this triple
"PROCESSING..."thing about? If not, why doesprintlnclaim so?
LazyList only knows when elements are evaluated because you forced them through accessing them in LazyList. In the case of using LazyList.apply, the following happens:
LazyList.apply is initially calledLazyList.apply calls LazyList.from, which is going to create a LazyList by lazily iterating through the intermediate Seq materialized in step 1By the time step 2 has finished, the LazyList doesn't know that its contents are evaluated. Furthermore, the spine of the list is itself unevaluated.
:sprint in GHCi isn't a great comparison because it is much more omniscient in its understanding of when things are evaluated. It does this by crawling the runtime heap and printing _ when it runs across thunks. By comparison, println just calls out to LazyList#toString, which is a regular Scala method.
Try with #:: constructor
scala> fun(1) #:: fun(2) #:: LazyList.empty
val res0: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>)
#:: takes by-name argument unlike LazyList.apply which takes by-value.
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