[MUSIC] In this segment, I want to show how we can use our understanding of lexical scope to avoid unnecessary recomputation of things when we're using closures. So, here's what we know. We know a function body is not evaluated until the function is called. That's not even relevant to lexical scope, that's the way functions and methods work in every language. You don't evaluate the body until you make the call. We also know that the function body is reevaluated, if you will, every time the function is called using the arguments for that call. But we also know that a variable binding evaluates expression, its expression once when the binding is evaluated, not every time the variable is used. These are three things we've definitely seen, definitely used. And if you take a minute to understand exactly what's being written here, you probably understand those three things as well. Now, I want to put those three things together to point out that with closures, we can use these things to avoid repeating any computation that you would naturally put in the function body, but is not actually using any of the function's arguments. This can improve performance. It can make your code a little longer. It can make it more or less readable, depending on your preference, but it's a good example that helps emphasize the semantics of how functions and function closures work. So, let me show you the example I'm going to use to demonstrate this. First, I just have our old friend, the very famous higher-order function filter, no change there. And now, I'm going to use filter with this function here, it takes in a list of string xs and a string s and it returns another string list. So, this has type string list star string arrow string list, okay? and what it does is it filters out all the strings that are longer than s, okay? So, all we're going to do is call filter with xs for the list and with this perfectly reasonable anonymous function, take in an x, that'll be an element of the list and ask, is its size less than the size of S? If it's strictly shorter, then we'll have it in our output and if not, we'll filter it away, alright? There's nothing wrong with this, this will produce the right answer. But it turns out that we are going to recompute the size of s for every element of the list xs. Because filter is going to call this function, one time for every element of the list And and time we call it, we compute the size of x, which we need to, x is changing every time, and the size of s. But that's an unnecessary recomputation. The size of s is not going to change. The way to fix that is as we see in this second example. Let's just create a local variable that holds the size of s, call it i, alright? And now in our anonymous function, just ask, is the size of x less than i? This will produce exactly the same answer. There's nothing any caller can do to tell whether we use the first version or the second version, except to notice that if our list is very long and/or if the length of s is very long, we could see a performance difference. So, that's exactly what I wanted to show you and notice we need closures here. If we don't have lexical scope, if we don't store with this closure, the environment where the function was defined, we're not going to be able to use this variable i. So, this is yet another example of where closures and lexical scope are very natural thing. So, I wanted to show you this, that it actually works but it's a little hard to do because you can't see the difference in all shorter than one and all shorter than two. So, for the purpose of showing you the difference, how about we put in some print statements so we can see where things happen. So, I haven't done this with you before but there is a function Print in ML that takes a string and prints it out. There's also this semicolon operator. when you use it as an expression in general, let me put this in a comment here, if you have e1;e2,e2, it does e1, throws away the result then does e2 and that's the result for the whole thing. In functional programming, you don't need this sequencing very often because what's the point of performing a computation that wouldn't have assignments of side effects if you're just going to throw away the result. But printing is a good example where it does have a side effect and that's what we're actually trying to do here. And similarly, let us put in a print before this call. So, my idea here is to print one exclamation point before any time that we call string.size of s. So, in fact, let me change this up here. I like it a little better if I put it right here so you can see it really is related to the size of the string s, okay? So now, I have my two functions. And now, down here, I have some testing code where I had the prints commented out, alright? So, what it's going to do, it's going to, it's going to print with allshorterthan1 and then it's going to call allshorterthan1 on an length four list with this string. And then, allshorterthan2 on the same list and the same argument, okay? And if we just run this, say use closures_and_recomputation, it's going to end up doing those print statements before the other stuff from the RPL as soon as I fix my parentheses here somewhere. yes. One too many there. or [SOUND] let's see. Try it that way. Yup. Good. And we see that with width allshorterthan1, which was the version that did not use the local variable, we got four exclamation points for our length four list. But when we called allshorterthan2, we only did this let binding a single time. Once, when we computed the size of s and even though we called this function four times, we looked up i four times but looking up i just returned, I think three, the length of that list. There's no recomputation when you look up a variable.