-
Notifications
You must be signed in to change notification settings - Fork 11
Lazy Evaluation
Laziness means do things as late as possible, and never do them twice.
Streams solve the problem of unnecessary computations by not computing the tail of the stream unless required. But suffers from a serious potential performance problem: If tail
is called several times, the corresponding stream will be recomputed each time.
This problem can be avoided by storing the result of the first evaluation of tail
and re-using the stored result instead of recomputing tail
.
This optimization is sound, since in a purely functional language an expression produces the same result each time it is evaluated. We call this scheme lazy evaluation (as opposed to by-name evaluation where everything is recomputed, and strict evaluation for normal parameters and val
definitions.)
Scala uses strict evaluation by default, but allows lazy evaluation of value definitions with the lazy val form:
lazy val x = expr
This causes x to be evaluated lazily. What this means is that x
is evaluated only when its referenced for the first time. For all later times that x is referenced, the same value is used. This is unlike:
def y = expr
where y
is also evaluated only when its referenced for the first time, but y
is also evaluated every later time it is referenced.
def expr = {
val x = { print(”x”); 1 }
lazy val y = { print(”y”); 2 }
def z = { print(”z”); 3 }
z + y + x + z + y + x // last line
}
expr
what gets printed as a side effect of evaluating expr
?
Solution:
- x is printed right away, as it is by value:
x
(even before the last line comes into play) Now we start with the last line i.e.z + y + x + z + y + x
- z is printed as it is referenced:
z
- y is printed as it is now referenced referenced:
y
- x is already evaluated, so nothing is printed
- z is printed again as it is reevaluated on every reference:
z
- y is already referenced once, so nothing is printed
- x is already evaluated, so nothing is printed
Finally: xzyz
In the implementation of Stream
in Scala, the tail
is implemented as a lazy val
so that it is evaluated once when it is referenced, and then re-used everytime it is referenced again.