Question regarding lazy evaluation


#1
Here is an example of two functions presented in the Book,
Functional Programming In Scala. I am curious about the output of the second
example, maybeTwice2. Why are there not two "hi" s printed?  I am ware that
the purpose of the example is to illustrate lazy evaluation. But still wonder why "hi"
is not printed twice. Can some scala guru please explain?

def maybeTwice(b: Boolean, i: => Int) = 
if ( b ) {
i + i 
} else 0

scala> val x = maybeTwice(true, { println("hi"); 1+41 })
hi
hi
x: Int = 84


scala> def maybeTwice2(b: Boolean, i: => Int) = {
lazy val j = i 
if ( b ) j + j else 0 
}
scala> val x = maybeTwice2(true, { println("hi"); 1+41 })
hi
x: Int = 84
scala>

#2

actually , I think I understand it now. The first time it was touched it was evaluated and printed the hi. The next time it was called it just returns the result of the first evaluation, which is simply, 42. DUH


#3

Possibly, you meant “d’oh!” Lazy semantics is often more “d’oh!” than “duh”.

Its purpose is to let the system figure out the order of evaluation.


#4

The result has nothing to do with laziness. Laziness only comes into play if you set b to false.


#5

The lazy thing stores the value, or caches it , for a later evaluation. I think.
If you do not use “lazy” it will be evaluated twice; with lazy it is evaluated once.


#6

That’s just not true – if you leave off the “lazy”, you get exactly the same result. (Try it.) It’s the val that causes the difference in behavior, by forcing the computation to happen once, when you evaluate that val; the fact that it’s lazy is irrelevant.

Basically, the only thing that matters is how many times i is encountered in evaluating the function. Laziness doesn’t change that…


#7

A good intuition is that a by-value (i.e., “normal”) argument behaves like a val in the body of the method (it’s evaluated once, before entering the method); and a by-name argument behaves like a def (it’s evaluated each time its value is needed). Martin explains this very well in the Functional Programming Principles in Scala course on Coursera, which you might find useful.


#8

Actually lazy gets around the “evaluation every time it is needed” issue by caching the by-name object and thus, using it only once when it is called. If the lazy variable is called again it will only use the cached value. Paul Chiusano and Rúnar Bjarnason explain it very thoroughly in the Book: Functional Programming In Scala. ( However, that book is not a fun book to read for a novice like myself; though I do like a challenge )


#9

hmm maybe you are right. I will check that example again and see.


#10

Here is a trace, also I added an additional function:
def maybeTwice(
b: Boolean,
i: => Int) = {
println(“hello 1 before usage”)
if (b) i + i else 0
}

def maybeTwice2(b: Boolean, i: => Int) = {
lazy val j = i
println(“hello 2 before usage”)
if (b) j + j else 0
}

def maybeTwice3(b: Boolean, i: => Int) = {
val j = i
println(“hello 3 before usage”)
if (b) j + j else 0
}

def main(args: Array[String]) {
var x = maybeTwice(true, { println(“hi 1”); 1 + 41 })
println("=============")
x = maybeTwice2(true, { println(“hi 2”); 1 + 41 })
println("=============")
x = maybeTwice3(true, { println(“hi 3”); 1 + 41 })
//println("x = " + x)
}

observe the output carefully:

hello 1 before usage
hi 1
hi 1

hello 2 before usage
hi 2

hi 3
hello 3 before usage


#11

Right, in fact @sidhartha11 you can test this by using def j = i instead of val j = i. It will print the output twice. In other words, the lazy is a red herring in that particular example.


#12

Correct – lazy affects the order of evaluation. But that has nothing to do with the fact that it’s operating on a by-name parameter – lazy will always do that.

Basically, you’re observing two entirely separate phenomena. The by-name parameter means that it will be evaluated each time it is referenced, so you sometimes want to stick that in a val so that it only gets evaluated once. The lazy affects when (or indeed, if) the evaluation of its right-hand-side happens. But they’re independent effects, neither of which depends on the other, and it’s very important to keep them straight.

I actually don’t recall if I’ve ever used them together, although it’s reasonable to do so – it’s a way of making sure that the by-name gets evaluated at most once…


#13

There is an issue for lazy params, i.e., a by-param that is assigned to a lazy val. Oh, I found it


#14

Wow, that is one old issue. And yeah, it would be nice to have…


#15

The example comes from “Functional Programming In Scala” , It is a strange book to attempt to read; especially if you are a novice. However, I feel that the book is actually helping me slowly understand some of the unique aspects of functional programming in general; something that is really new to me. I only recommend that book to novices that have a joy of experiencing DEEP MENTAL PAIN.


#16

Totally true ! laziness has nothing the do with the fact that the value is evaluated twice