In this session will introduce the concept of lazy evaluation, which is some language support and one time constructs to make lazy computations more efficient. So we've seen that the previous implementation of tail lazy list suffers from a serious potential performance problem if tale is called several times, the corresponding lazy lists will be re computed each time. This problem can be avoided if we store the result of the first evaluation of tail and reuse the stored result instead of re computing stale another time in a purely functional language this optimization is sound since the tail expression will produce the same result each time it is evaluated. So that means there's no need to actually do the evaluation several times since we know that every value will be the same, we can just use the first value that we have computed. We call the scheme lazy evaluation and that's supposed to the by name evaluation in the case where everything is re computed and strict evaluation or for parameters by value, evaluation for normal parameters and value definitions, so by default scholar is strict by value. It has by name parameters that evaluate each time they are called and we now also want to have lazy evaluation where we say we evaluate zero times or once but not multiple times. Some functional languages, including Haskell, use lazy evaluation by default Scala on the other hand, uses strict evaluation by default but it still allows lazy evaluation of value definitions with the lazy Val form so you can write a lazy in in front of a Val and then the value definition is as usual. And what that means is that the right hand side of the Val definition will be evaluated the first time X is used instead of immediately so, as an example, let's consider the following program that you see here. So I have def expert and then a Val and then a lazy Val and then a def and then I sum them all up like this and for each definition, I actually print what gets evaluated on the right hand side. If you run this program, what gets printed as a side effect of evaluating expert so, when I evaluate expr, I have a strict value definition X so, I evaluate its right hand side immediately, which gives me an X so, X is the first thing that is printed. The lazy Val will not yet be printed because it's lazy right hand side of the death will not yet be printed because it's a def. So, then we look at the expression here so, now we call Z that print Z so, we have an X followed by a Z now, we evaluate Y so, that would print Y? Now we want the value of X that's already evaluated so, nothing gets printed now, we want the value of Z again so, that will re evaluate the right hand side that will print another Z. Now, we want the value of Y again, in this case because Y is lazy it is already evaluated it was already evaluated once so nothing gets printed and finally we want the value of X again, which of course is evaluated so, again, that's what we get on the right hand side, XYZ. So lazy Val allow us to overcome our performance problems for lazy lists so, taillazylist.cons can now be implemented like you see here instead of a def tail equals tail we have a lazy Val tail equals tail that's all that's needed really. That means that the first time somebody calls tail we will evaluate the call by name, parameter Tl but the next time somebody wants tail we have the value already stored here so, further evaluations of tail are avoided. So this laziness business might look a little bit like wizardry to you in the sense that it's pretty smart at avoiding computations to see it in action we can just essentially use our basic substitution model and verify step by step that indeed laziness does what it promises. So let's start with the original expression that we have lazy range 1000, 10,000 filter is prime apply one, we want to reduce that from first principles using our standard rewrite rules and then verify how much of the list 1000 to 10,000 actually gets constructed. So we first expand lazy ranch that gives us this expression here with the parameter substituted and then we plug that into the rest of the expression that you see here. Now we simplify the if then else so that reduces to the else part which you see here so, one thing that's important here is that this expression here hasn't been reduced yet it's left as it is because it is a call by name, parameter to cons. Let's abbreviate this expression to just C1 so, we have C1.filter is prime, apply one so, we now expand filter on the C1 value, which gives us here the right hand side of filter with his prime method at the right place here. So if you simplify that, then we know that C1 is not empty so it's going to be this one here if is prime C1.head then this cons expression Otherwise,C1.tail.filter. Now we evaluate C1.head so, that gives his prime 1000 and if you follow the computation of its prime, we will see that this reduces to false so, we are left with this expression here. Now we can simplify the if then else so, we are left with the C1.tail.filter is prime apply 1 and now we go back to the actual argument of the C1 expression that corresponds to the tail that was lazy range of 1000 plus 1, 10,000. So at this point we compute that 1000 plus 1 is 1001 and we plug it into the expression here. So it's lazy range 1,001,000 filter is prying apply 1, which is basically what we started with except that we have a 1 here now and that evaluation sequence continues like this until we hit this situation here where we have lazy range 1009, 10,000 filters, prime apply 1. And now by the evaluation of lazy range we get a cons 1009 and this expression here, filter is prime if we abbreviate that to C2 and we are left with C2 filters, prime apply 1 now, 1009 is a prime number. So we get the second case that we get our prime numbers consists of cons followed by the tail and now we have to finally evaluate the apply method we get there so, let's assume applies to def like this so, if N equal zero then head as still apply N minus 1. So what we would get here is for the right hand side of the apply method 1 equals 0 because 1 is the index here so, that's false so, we continue with the cons in the else part that you see here and that means now we have a cons to tail because that comes from the apply method. So we take essentially this tail of the cons which gives us C2.tail.filter is prime, apply 0 and that by computing what is the tail of C2, we get lazy range 1010, 10,000 filters, prime apply 0. And that process continues until finally we have lazy range 1,013,10,000 filters, prime apply 0 that then gives the cons expression as before so, it's cons 1013, lazy range of the rest filters prime apply 0. Let's call that C3 if we filter that expression we get cons 1013 and then this tail because 1013 is in fact the primes or the filter will include it as a first element and that 1013 is the final result. So if we look at our rewrite trace, then we will see that indeed, we have never constructed a lazy list beyond the elements1013 so, we have computed exactly as many elements as we needed to verify that the Second prime number following 1000 is 1013. Good so the simplified implementation of lazy list that we have worked with has a lazy tail, but not a lazy head, nor a lazy is empty, the real implementation of Lazy list is lazy for all three operations so, lazy list is a lot lazier than tail lazy list. To do this it maintains a lazy state variable a bit like this so, there is a class lazy list in the standard library, it has essentially an init parameter that computes the state of the lazy list. And the state of the lazy list is either that it's empty or that it's a cons of ahead of type T, and tail which is a lazy list. But that state of the the lazy list knows whether it's empty or cons and what the head is that is altogether computed lazily by this by init parameter, so that's the essential difference between the two.