Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why scala's collections are not 'views' by default?

In some cases, applying 'view' to collection before doing map/filter/... can decrease performance. However, those situations are (afaik) quite seldom, for example, when there is a single operation.

But most of time appending '.view' can give a performance boost,by preventing from creating intermediate collections.

So, why 'view' is not applied to collections by default? Am I missing some hidden costs of it?

like image 966
dveim Avatar asked Jul 06 '15 20:07

dveim


People also ask

What is view collections?

A collection view manages an ordered set of content, such as the grid of photos in the Photos app, and presents it visually.

What is view in Scala?

The View is a special kind of collection in Scala that takes a base collection and executes transformer methods on that collection lazily. We can turn every Scala collection into a lazy representation and back via the view method.


1 Answers

Short answer: Views are not always beneficial, and strict behavior by default can prevent surprises.


Consider this snippet from the early collections documentation:

... you might wonder why have strict collections at all? One reason is that performance comparisons do not always favor lazy over strict collections. For smaller collection sizes the added overhead of forming and applying closures in views is often greater than the gain from avoiding the intermediary data structures.

Consider:

List(1, 2).map(_ + 1)
// vs
List(1, 2).view.map(_ + 1) 

The view uses just a little more overhead, and with some crude measurements is about 4% slower (a small, but noticeable difference).


The documentation continues to say:

A probably more important reason is that evaluation in views can be very confusing if the delayed operations have side effects.

Consider:

for(i <- (1 to 10)) println(i)

If (1 to 10) were actually a view (it is not), nothing would happen until it was forced to do so. Code that looks straight-forward might not be.


Worse, you may end up with surprising situations where you end up re-evaluating code you did not need to. Take this naive example:

// Evaluates to List(2, 3, 4, 5), prints "eval" during each evaluation
val list = List(1, 2, 3, 4).view.map { i => println("eval"); i + 1 }

// For each element in the list, prints the elements that *not* that element (in a view)
def printList[A](list: Traversable[A]): Unit = 
    list foreach { i => list.filter(_ != i) }

Because list is a view, printList is evaluates list n - 1 more times than it should!

scala> printList(list)
eval
SeqViewMF(...)
eval
SeqViewMF(...)
eval
SeqViewMF(...)
eval
SeqViewMF(...)

Now imagine list not clearly labelled as a view (i.e. a view by default). I can picture many strange bugs or bottlenecks occurring from unintended re-evaluation.

This is not to say that strict evaluation doesn't come with it's own surprises. It was just a design choice that strict evaluation is less surprising than lazy evaluation (Paul Phillips may disagree).

like image 142
Michael Zajac Avatar answered Oct 01 '22 14:10

Michael Zajac