[MUSIC] In this segment, we're going to begin emphasizing in our semantics when expressions get evaluated. This is a key concept in programming languages and it's going to be essential for understanding the programming idioms we're going to see in the next few segments. So for every language construct in every programming language, the semantics does have to specify if or when those subexpressions appear to get evaluated. For two crucial examples, let's focus on function calls and conditionals. So in ML and Racket and other languages you're probably familiar with, when you have a function call, the arguments to the call are evaluated before the call starts. Before you evaluate the function body, each of them is evaluated once, and then the function body uses variables, the function parameters, to look up the results of those computations that already occurred. Conditionals work very differently. If you look at an if that takes three expressions, the test, the true branch and the false branch, we do not evaluate all of them eagerly like that. We evaluate just the first one and then use the result to decide which of the other two we evaluate, and then the other one is never evaluated. So we have not emphasized this quite as much before, but this piece of the semantics of language constructs is essential for programming correctly. So I'm going to switch over to the code, which I had written out here, and show you a number of examples that show that. So let's start with an ordinary function. A totally normal implementation of factorial. We've seen such an example many times. And if I click Run and use it, it will work as expected. Normal of 5. I'll get 120. And by the way, this function grows very fast and Racket has no problem with large numbers. So if I ask factorial 500, it's happy to print out this very large number that I will trust is 500 times 499 times 498 and so on, and everything works correctly. And what happened is when I called factorial normal of 500, if I had evaluated 500 that was a value, went into the body, decided x is zero, so, I will evaluate the false branch because x is not zero, excuse me. And so only because 500 is not zero do we decide to do this multiplication, and when we do a multiplication, we eagerly look up x we get 500, and then we do this recursive call, which after a whole lot of work returns a very large number, and then we do a multiplication, and then we return. So why am I emphasizing this? Let me show you a slight variation. Suppose I try to write a little wrapper for if. So I have a function, my-if-bad. It takes three arguments, e1, e2, and e3, and then it just calls if with e1, e2, and e3. So it looks like it's just an unnecessary function wrapping of if. And here's a version of factorial bad that uses my-if-bad. Now I encourage you to try this on your own, I'm not going to do it while recording. But if you call factorial bad with any number whatsoever, it will never terminate. It will never terminate. The reason is you call factorial bad with an argument, 0, 500, 5, I don't care what it is, -7, and the first thing it has to do is call the my-if-bad function. And before you call a function, you evaluate all three arguments. So, you see if x is 0, that will go quickly. You evaluate 1, that goes quickly. But then you have to evaluate this as well. And that will do a recursive call, and that will do a recursive call, and we'll never end the recursion. So it's essential to all of our recursive functions that if, as we saw up here in the good version, does not evaluate both of its branches. It only evaluates the one we need. The my-if-bad being a function, evaluates both. So it turns out that I'm going to make a third version of the example which is actually going to work. This is not good style, the first version is good style. But it's going to introduce an idiom that's going to prove very useful to us in upcoming segments. And that is, if you really want a function that takes three arguments and acts acts like if, you have to change what it takes for some of those arguments. Have it take for e2 and e3 not the result you want, but a zero-argument function, that if you call that function, you get back the result you want. So what my-if-strange-but-works does is it evaluates e1 to get an answer, and then it either calls e2 with no arguments, or calls e3 with no arguments. Because remember in Racket if you want to call a zero-argument function that's in some expression, you write e inside a parenthesis. So e2 and e3 and any call to my-if-strange-but-works have to themselves be zero-argument functions. So, we're doing higher order functions here. And now, if you want to write factorial using my-if-strange-but-works, you can do that. Let me just make this on the screen for you. So, factorial-okay takes in a number x. Says, if x calls my-if-strange-but-works with three arguments, the result of is x zero, this function and this function. So, functions do not evaluate their bodies until you call them, we know that. So all three of these arguments evaluate very quickly. Is x is zero, that's very fast, these other two are already values, we just make the right closure, and then we call my-if-strange-but-works, and my-if-strange-but-works decides which one of these lambdas to call, and it never calls the other one. And if you never call a function, its body is never going to execute. So this works just fine, if I say factorial-okay of 500, I will get my very big number, okay? So, that is the code I wanted to show you. Let me emphasize yet again what is going on, and what we call these things. There's some terminology I want to introduce related to this idiom, okay? So we know how to delay evaluation. If you have an expression that you don't want to evaluate yet, you want it to evaluate later or maybe not at all, put it in a function and don't call the function. Thanks to closures, you can really do this anywhere. Anywhere you want, you can just take some expression e, replace it with lambda, take no arguments of e, and now you have a procedure that when you call it, will produce the answer then, but you do have to remember to call it. So that's what we saw here. You see it on this side, compared to the example that did not work, our definition of the my-if function had to remember to either call its second argument or call its third argument, and then our use of it in factorial had to not pass in the expressions that we wanted to evaluate, but to pass in zero-argument functions, that we could then call to get the answers we wanted. So, we have names for this. Actually, let me go back on this slide here. I forgot to say this middle part I wanted to say. So when you add these zero-argument functions, we actually have a name for them. They're called thunks. You can read on the Internet where people think this word came from. Nobody is quite sure, or at least I have not heard a definitive account. But it's just a funny word that computer scientists use. A zero-argument function that is used for the purpose of delaying evaluation in this way is called a thunk. You can even use this as a verb, if you want to tell someone no, no, no, you are evaluating that thing too eagerly, we might not need it passing a function that produces the result, you can just say, thunk that, thunk the expression, or the function needs a thunk. You can say all of these things, okay? So let's emphasize three very different things in Racket. They look similar, but it's essential we understand the difference, or your going to find it very difficult to do your next homework assignment. When you have some expression e, we evaluate it and we get a result. So maybe e is plus three, four. We evaluate that, we get seven. But if instead we have a thunk with e in it, this is a function that will not evaluate e at all until you call it. And then every time you call this thunk, we will evaluate e and get the result. So lambda no arguments plus three, four, is a procedure that when you call it, you get seven. And how do you call a thunk? It looks strange for people not used to parenthesis, but if you put e in parenthesis, we evaluate e to get a thunk, and then we call the thunk that evaluates the thunk's body, and that is our final result. And we've seen this, this is why you if you put 37 in parenthesis you get an error. Because 37 is not a thunk. And if you put something in parenthesis, you're calling it with zero-arguments. But if you had lambda parenthesis parenthesis 37, then we could call it, so you see here with parenthesis around it, and you would get back the 37. So we haven't done this in a useful way yet, the if is built into Racket is the right thing to use, but the upcoming segments are going to use thunks to code up some very powerful things in Racket, to use the idea that often delaying or avoiding a computation is exactly what you want to do.