How to lazily evaluate collections in Scala


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 collection (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.

 

KNOLDUS-advt-sticker

This entry was posted in Scala. Bookmark the permalink.

2 Responses to How to lazily evaluate collections in Scala

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s