How to lazily evaluate collections in Scala

Table of contents
Reading Time: 2 minutes

The Scala language provides to ways to implement the collections. One is strict and other is non-strict or lazy.

Whenever, the instance of a collections in scala(except for the Streams) is created, it creates the strict version of the collection which means memory is allocated at the same time.

A simple example of strict collection is:


val list = List(1,2,3,4,5,6,7,8,9,10)
list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

The above code, the memory would be allocated for the List immediately.

But whenever a view in the collection is created, it makes that collection as a lazy collection which means memory allocation would not be done at the time of initialization, instead, it would take place whenever they are actually accessed or some transformations are applied on them.

For example,


val listView = List(1,2,3,4,5,6,7,8,9,10).view
listView: scala.collection.SeqView[Int,List[Int]] = SeqView(...)

Here, the type of listView is SeqView[Int, List[Int]], which makes a lazy collection.

The signature of SeqView is explained as:

  • Int is the type of elements in the view.
  • List[Int] is the output we get when it is to be taken back to strict-collection.

Views take very little space in memory, it contains only the definition, not the copy of all the data that it represents.

Now, lets take some transformations on lazy collections and see how the type changes.


val view = List(1,2,3,4,5,6,7,8,9,10).view
view: scala.collection.SeqView[Int,List[Int]] = SeqView(...)
val sqList = view.map(n => n*n)
sqList: scala.collection.SeqView[Boolean,Seq[_]] = SeqViewM(...)

The first line is self explainable and its type also. But, the second line, i.e., when applying a map transfoamtion on the view, the type of sqList becomes SeqViewM.
The trailing ‘M’ indicates that the view encapsulates a map operation.
Similarly, applying reverse on a view:

val reverseList = view.reverse
reverseList: scala.collection.SeqView[Int,List[Int]] = SeqViewR(...)

Yields SeqViewR, where trailing ‘R’ means that a reverse operation has been applied on some view.
Applying, two map functions back to back yields SeqViewMM type, similarly, using filter on view yields SeqViewF type.

This is how, the transformations are applied on views. But, how do we finally see the result out from a view ? Well, that’s simple.
Simple, use force method on the view.

reverseList.force

And, immediately the REPL will output as:

List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

Views and Streams are different from each other. Streams are not processed until any transformations are applied on it. But, views are lazily evaluations and processed only when forced to do so.


val streams = Stream.range(1,10)
streams: scala.collection.immutable.Stream[Int] = Stream(1, ?)
val views = List.range(1,10).view
views: scala.collection.SeqView[Int,List[Int]] = SeqView(...)
val streamRightResp = streams.takeRight(2)
streamRightResp: scala.collection.immutable.Stream[Int] = Stream(1, 2, 3, 4, 5, 6, 7, 8, 9)
val viewRightResp = views.takeRight(2)
viewRightResp: scala.collection.SeqView[Int,List[Int]] = SeqView(...)

The above code snippet, when takeRight(2) is applied, all the elements before that index (i.e 10-2=8) are now in memory, but this was not in case with views. If one wants to access the element at nth index from Stream, then all the element before the nth index has to be in-memory.

Views can be used when:

  • a large collection is not needed to be in memory.
  • There is no need of intermediate collections, which are created every time a transformation is applied on strict collections.

collections

Written by 

Harshit Daga is a Sr. Software Consultant having experience of more than 4 years. He is passionate about Scala development and has worked on the complete range of Scala Ecosystem. He is a quick learner & curious to learn new technologies. He is responsible and a good team player. He has a good understanding of building a reactive application and has worked on various Lightbend technologies like Scala, Akka, Play framework, Lagom, etc.

2 thoughts on “How to lazily evaluate collections in Scala3 min read

Comments are closed.