Lazy evaluation in Scala

an important concept in functional programming


19 Nov 2017 View Comments
#lazy #evaluation #scala #functional #programming

I recently came across some lazy evaluations while I was working on the Scala code which is an opposite idea of eager evaluation (strict programming language). Eager evaluation (or strict programming language) runs an expression immediately as the code is being executed whereas lazy evaluation would postpone the computation until we absolutely have to execute it. Lazy evaluation can really be beneficial which can save the computation time by folds (we can avoid it from even running at all). In my opinion, the word “Lazy” isn’t really suitable. I think more appropriate name would be to use “Efficient” instead.

In an absolute pure functional programming language like “Haskell”, the language is born lazy (non-strict). So the language evaluates and automatically decide to execute an expression or not. Unfortunately, Scala isn’t a pure functional language. Surprised? Since Scala is not a pure functional programming language (for being very closely tied to JVM), evaluation is eager (strict) by default. Therefore, Scala evaluates expressions as soon as they are available. However, with Scala, there is an option to declare a variable lazy way with the “lazy” keyword, meaning the evaluation happens when it’s required.

Here is an comparison example of using the keyword “lazy” vs not. Let’s first take a look at an example without the lazyness:

def printHi():Boolean = {
    println("Hello")
    true
}
val result = printHi
if (false && result) {
    println("yes")
} else {
    println("no")
}

Output:

Hello
no

To explain the above code execution flow, the code first executes the printHi function and prints “Hello” then stores the return value “true” to the result variable. It then executes the next line by calling, if (false && result) {. It sees the “false” immediately which eventually prints “no” in the else block.

You can tell immediately that it wasn’t required to execute printHi function at all. What if this printHi function was really expensive to run (say it runs for a few seconds)? Then everytime when this line of code is executed, we will need to execute this unnecessary function. We are basically running the function inconsequentially. In Scala, we can use the “lazy” keyword to handle this kind of situation. Let’s try to do the same thing as above but by giving an explicit “lazy” keyword to the result variable:

def printHi():Boolean = {
    println("Hello")
    true
}
lazy val result = printHi
if (false && result) {
    println("yes")
} else {
    println("no")
}

Output:

no

When you execute the code above, the result variable gets identified as a lazy variable. When the if statement is executed, it doesn’t even run the result as if statement sees the false right away. Then the code eventually prints “no”. You can see from the output, it doesn’t actually print “Hello” this time as it was never executed.

Using the explicit false in the first condition of the if statement is used just for the illustration. It could just be a condition that results in false most of the time. Even when the first condition generates true most of the time, as long as there is a chance of it generating false, lazy is probably is a better choice. Now you might ask, does this “lazy” code even get executed when its necessary? of course. Let’s tweak our code by making the first part of the if statement to “true” instead of “false”:

def printHi():Boolean = {
    println("Hello")
    true
}
lazy val result = printHi
if (true && result) {
    println("yes")
} else {
    println("no")
}

Output:

Hello
yes

Walking through the above example, we first hit the lazy keyword and set aside executing the result variable. When we hit the if statement, the if statement sees the first boolean as true and tries to execute the result which is declared as lazy and will eventually execute the printHi function (printing “Hello”), then it prints “yes” as it enters the if block. So the runner basically postponed the computation until we absolutely had to execute it.

In Scala, there are other ways to be lazy than to use the actual keyword lazy. First of all, in Scala, “lambda” is just by default is lazy. When you pass in a function as a parameter, it will not be executed until it is required in the code.

Second of all, there are non-strict collections aka lazy collections (such as the type “Stream”, a non-strict linked list) to be explicit. In Scala, any collection can be made non-strict with the view method. Here is an example which doesn’t use the view method:

val student = List(
("Joe", 87),
("Bob", 91),
("Frank", 60),
("Saram", 95),
... say we have around 100 tuples
)

def gradeHigherThan90(person: (String, Int)) = {
   println(s"Higher than 90 for $person")
   person._2 > 90
}

def nameMatchingLetterCount(person: (String, Int), len: Int) = {
   println(s"Letter has $len for $person")
   person._1.length == len
}

println(student.filter(gradeHigherThan90).filter(nameMatchingLetterCount(_, 5)).head)

Output:

Higher than 90 for (Joe,87)
Higher than 90 for (Bob,91)
Higher than 90 for (Frank,60)
Higher than 90 for (Saram,95)
... Go through the list filter out people higher grade than 90
Letter has 5 for (Bob,91)
Letter has 5 for (Saram,95)
Letter has 5 for (Calvin,100)
... Out of those people with 90, get the first (head) person who has 5-letter name
(Saram,95)
Elapsed time: 1304887ns

By the way, I have added a timer to our code just to compare between the executions. As you can see, the code is basically iterating through all the elements in student list (of tuples) which could have millions of data (in extreme cases). It is an overkill, just to find that first person with the higher grade than 90 then search for name containing 5 letters then find the one filters to both. This is where the view method comes handy. The view method produces a lazy collection. You can tweak the print statement like this: “println(student.view.filter(gradeHigherThan90).filter(nameMatchingLetterCount(_, 5)).head)” which would produce an output like this:

Output:

Higher than 90 for (Joe,87)
Higher than 90 for (Bob,91)
Letter has 5 for (Bob,91)
Higher than 90 for (Frank,60)
Higher than 90 for (Saram,95)
Letter has 5 for (Saram,95)
(Saram,95)
Elapsed time: 844147ns

Note the execution time is a bit smaller (although it really depends on the # of executions) since the code execution is minimalized (# of executions is less for the “view”) by running whatever is necessary. It simply didn’t iterate the entire collection. It picks the first element matching the first filter, matches with second filter. If not matched for both, it moves onto the next item matched in the first filter then match against second filter. When both filters are met, it found the first element (head) that matched to the both filters.

The concept of “lazy” is really useful in programming for building an efficient software, together with input size and frequency of code being executed.

Share this post

Me

I am a passionate programmer working in Vancouver. I strongly believe in art of algorithms and together with it to write clean and efficient software to build awesome products. If you would like to connect with me, choose one from below options :) You can also send me an email at