3
votes

I think this question is essentially about what laziness is in the context of mutability.

In Programming in Scala (or the docs), they give examples of how to use views on immutable and mutable collections. In that section, they state

[transformer methods] take at least one collection as their receiver object and produce another collection in their result. ... A view is a special kind of collection that represents some base collection, but implements all of its transformers lazily.

They give examples with immutable collections and I understand how laziness works there. But I'm struggling with the role laziness plays in the example with a mutable collection:

Many transformer functions on [views over mutable sequences] provide a window into the original sequence ... an example ...

val arr = (0 to 9).toArray
val subarr = arr.view.slice(3, 6)

The view does not copy these elements, it just provides a reference to them.

def negate(xs: collection.mutable.Seq[Int]) = 
  for (i <- 0 until xs.length) xs(i) = - xs(i)

negate(subarr)
arr  // Array(0, 1, 2, -3, -4, -5, 6, 7, 8, 9)

I understand why we get that answer. But what is lazy about the transformer slice? I had understood laziness to mean that values are only computed when needed (like with the immutable collections examples). But the values in slice are never computed, they are just references to those in arr, even after calling negate. Is this my misunderstanding of laziness? Or are the docs using laziness in another way? Or something else?

1
Every lazy thing that happens with the immutable collections happens with the mutable collections too.user2357112 supports Monica

1 Answers

0
votes

Here's a better example of this lazy behavior:

val a = Array(1,2,3)
val b = a.map(_ + 5)
val c = a.view.map(_ + 5)
println(b(1)) //prints 7
println(c(1)) // prints 7

a(1) = 5

println(b(1)) // still prints 7, since that array was computed on instantiation
println(c(1)) // now prints 10, since elements of c are lazily evaluated each time.