 We just did our Thanksgiving pop quiz. Now we have a question. Can we know their type difference? Guys, you guys are laughing. They're laughing because the quiz was so easy. OK, so we're talking about a family meal there. We have a function foo a and b, and the result is just returning a calling b. So here the tree looks something like this. And so the question is, what's the type of a, what's the type of b, and what does foo return? Inside the array as well. The right-hand side of the array? Yeah. Well, that's exactly the same as other problems. I don't think that adds anything to the question. So the question is, here, what do we know about b? b is just type b. Yeah, it can be anything, right? Tb, it can be literally any type b. So then a, what's the type of a? Can a be anything? No. What does a have to be? It has to be a function, a function that takes in what? Type b. So it takes in type tb and returns what? And then so what does foo return? Type tb. Type td? Tc. Or foo? Tc. What would this mean? From tc. Yes, that it could be different from the return of a, and then it could be different from b as well, right? This is basically saying that foo returns some type that's completely unconstrained and not connected to any of the other types. But if it's tc, it means it returns whatever the return of a is. In the other case, it was the array element, but as long as you have whatever was in the array at the same time as what returned. Yes. This is the key is making sure that this tc, this tc are the same, and this b, tb, and tb are the same, and that this is a function in here. Is that on me or on you? You don't have to answer. Okay. Any other questions? Cool. Alright, you all are in for a treat because now we get to the heart of Lambda Calculus. So you, this is actually, I think, you may be the best way to do it. I'll try to maybe do this from now on is, because this really, we're getting into what does execution mean in Lambda Calculus. And really to understand this, you have to know everything that we've done up to this point. You need to know how to do substitution, you need to know how to disambiguate Lambda expressions, which is something that you don't need to know about free and bound variables, which are all stuff that hopefully you practice really hard on so that that way you did that very easily during good term three. Alright. And so now we're building on top of that and we're going to talk about how does Lambda Calculus actually simulate execution. So, and now what we're really doing is trying to formalize our intuition that we built earlier that said, okay, this Lambda expression means what does application mean? What does an abstraction mean? Right, so now we're defining, okay, what does it mean to actually call a function in Lambda Calculus? So what we're going to do is we're going to think about execution as a series of terms, a sequence of terms, and it's going to be the result of we can think of calling or invoking functions. So one step in this sequence would be we invoke and call one function, and the next one after that would be calling a second function, and then a third function, and so on. So each step in this sequence we're going to call a beta reduction. We're using these terms because that is what Lambda Calculus things use. So beta reduction, so every term in this step is one beta reduction. We can't apply this to everything, right? So if we have, I'm not doing anyone, right? If we have the Lambda expression x, y, are there any functions to call here? Application, but I just have two IDs. Is there any functions to call here? About like Lambda y dot y. You can think of this as declaring a function, right? This is saying that this is a function, Lambda y dot y, and it's defined as Lambda y dot y, right? There's no, can we actually reduce this any, like invoking or calling any function? No. No, but now if we have something like this, now can we reduce this? Yes, now we can actually invoke this function with the parameter x. So this is going to be called a beta reduction. So this means that expressions in an application form or the left side is a Lambda expression. So one question would be, is this in a beta reduction form? Because we're only, the function that we want to call is going to be on the left. This is one in application. Left versus right is important. And this is why associativity is important, right? We have x, y, z. If x happened to be a Lambda expression, this would be first apply y to the expression first, and then that result applies z to that. Cool. But this is not in a beta reduction form, right? There's no reductions we can do. So of this form, so this is what we're looking for. Lambda x dot e or e is obviously any Lambda expression, and x can be anything. So here x really is just a placeholder. It doesn't matter. All that matters is the left-hand side is an abstraction, and the right-hand side can be anything. Cool? So I think I made the analogy before to kind of potential energy, right? So this means that when you see something in this form, that means that it's the potential that we can do a beta reduction and reduce this by calling this function. And so beta reduction, now that we've gone through the, I don't even know how many, has it been three classes of work? What's that? A lot of minutes, 150 minutes, and a midterm on building up all this machinery. But now we can say kind of very simply what exactly we mean. Well, when we beta reduce Lambda x dot e with n, what we're going to do is take e, take the body, and substitute all x's with n. And specifically what x's are we replacing here? e because we've stripped off the abstraction, right? e is just looking at the body here because we know. So if we think about Lambda x dot e as a Lambda expression, all x's in there will be bound, right? There'll be no free x's in this whole expression. But if we take off the Lambda x, right, just look at the body, then we can say all the x's that are now free were bound to this Lambda x. And so we're going to replace all three x's with this new Lambda expression n. And the trick here, it seems simple, the trick is when we have to do rewriting, right? When we have the case that inside expression, when we try to do the substitution, there's a Lambda expression that is not what we're trying to replace. And that is a free variable in the end. So that's the edge case, the corner case that we always have to think about. So beta normal form is when we finally get to the end and we say, okay, we've done a series of beta reductions, and we can no longer do any more beta reductions. So you can think of it as we've reduced this function as far as we possibly can. So there are different types. So I talked about earlier that there are different types of ways. So you can think here, beta reduction is really just one step. You want to take one application and we're trying to reduce that one application. But there could be multiple, there's no restrictive on this. We could have a Lambda x.x, y over here, and then here we could have a Lambda z.zz, or parentheses x, something like this. So here there's two choices of which thing I reduce first. So there are, and it turns out, and we're not going to go into this, but it turns out that the different strategies you have for which one you reduce first correspond to different types of pass-by values and pass-by variables. But we're not going to get into that. We're just going to say, we're going to use what we think called full beta reduction. So we will reduce all reductions regardless of where they appear. This will mean that all the examples I give you will not matter which order you do the reduction. So it doesn't matter if you do the first one, the second one, whatever, you will always be able to reduce it fully. I think that also means I won't give you any problems with loops, because if you can't, otherwise it's just going to keep going forever and you'll never be able to fully beta reduce it. Cool. Questions on the terminology? The high-level ideas of where we're going to go. If we were just talking about arithmetic operations and digits, the beta normal form is just going to be this really ugly prefix arithmetic string. Almost. We'll see. Actually, we will define numbers, Boolean operation. We will define everything in terms of function. So a number will just be, as we'll see, a combinator. Depending on the structure of that combinator, it will represent different numbers. We'll get to that, probably not today, probably next Monday. But definitely we're going to get to Boolean conditions now. So you'll see we'll build up Boolean logic just using functions. Any other questions? Okay, let's step through some examples. Super simple example, lambda x dot x applied to y. So the first question we have to ask ourselves is can we perform a beta reduction here? On the left side of the application is an abstraction. Boom. We can perform this. So then what do we do? How do we do this? Take the expression out, which is x, and then do what? Substitute x with y. Substitute x with y. And we know how to do this. This is exactly one of the rules we have. We're just going to return y. So this is why substitution forms the basis of this computation. This is why knowing substitution is so important. So then we can do cool stuff like this. Lambda x dot x applied to lambda x dot x. So how many choices do we have here and what we want to beta reduce? The one is the outermost. We have an application. On the left we have a lambda expression. This is the body. And on the right is U and R. So why is this, this may have more than thrown people off. Why is this one not A? So this is an application. What's different? Yes, the left side is not an abstraction. The left side is just an ID. So there's no application. There's no beta reduction possible here. We're parsing. Disambiguation also comes in. So you know how to read this. So what do we do? Take the body of the abstraction here. So, remember, and this is also. So we go here all the way to the end. Here. So this is the body. So we're going to take x lambda x dot x and substituting x with U R. So what happens here? So we have an application. So we just distribute it to both sides. And then after we distribute it to both sides, this x will get replaced with U R. And what will happen with this one? Yeah, it will stay the same. Remember this lambda x because it's the same meta variable that guards that. So we'll get U R lambda x dot x. So what do we do? No. Because it's U space R, don't we just use U? I know it's an expression, but it's not the first where we sat replacement where we were replacing. I don't know. It seemed like at one of the times in the past, if we had done this, it would have just been a U that went into the. Cool. So let's look here. Yeah, let me copy this because the question is how is this different from this? So what's the difference here? Yeah, so this we have three applications, right? We have here the U and the R. So we have to use left associativity to group them. So we group these by here and then this with this. So we have one bigger reduction here, right? Where we will take this inside, replace x with U and then that result will be applied to R. So we do this reduction, we'll get the same thing, we'll get U, this protects it, I think I need more parentheses here. There we go. Can I reduce this? If we dealt though, should we be concerned? Yes. So how do you parse this? I'm drawing a tree. What's at the top level? Of course it's the expression. Which would be four types of expression. I was actually very good. E. E. So which abstraction or I think you only have two choices really. It's either an abstraction or an application. So what's at the top level? An application. So then what's this the rightmost expression? And what's the leftmost? So that will be U. Cool. And then what is this expression? Application as well. What's the left side? U. And the right side is line to X dot X. And this of course will then go down further to an abstraction. So the question is are there any applications here where the left side is an abstraction? Just an abstraction. That's the key. The outer most needs to be an abstraction. Because this is not an abstraction. This is actually an application first where the left side is U and the right side is an abstraction. And so we look here and say this is an application but the left side is lambda or the left side sorry is an ID it is not an abstraction. And we say here the left side is not an abstraction. It's an application. So we cannot reduce this any further. Cool. And when you go down. So now how many choices do we have? A person. Every click address. So it's going to be an expression at the top. So what's the first? What is this expression going to be composed of? Application. And what's the left side of this application? Abstraction. So it's going to be lambda X dot Y and the right side is going to be what? An application. So what's the left side of the application? And the right side? So how many beta reductions are possible here? Two. We have two allocations and the left side here is an abstraction and in here the left side is an abstraction. So here we actually do have a choice. So let's do... We'll do the innermost one. So we'll do this reduction. So we are going to take ZZ and we will replace inside of here the Z We will substitute Z with lambda W dot W. So we do this. The result is going to be lambda W dot W and lambda W dot W. So now how many beta reductions do we have? Two. Does this mean we've hit an infinite loop? We had two and now we have two? Yeah, we don't know yet. We haven't... So think about if we ended up back with lambda X dot Y Z dot ZZ, lambda Y dot Y or something that was output equivalent to all these functions. Then we get into a loop, right? Because we just keep outputting the same thing. But here we did this and yes, even though now we still have two choices in beta reductions we're not necessarily in a loop yet. So let's do the innermost one again. Because that sounds like fun. So here I'm going to replace inside the body here, this W. I'm going to replace it with lambda W dot W. So that's going to give me what? lambda W dot W. So now how many choices do I have? One. So now what's going to happen when I do this? So you can think of this. This is a function that takes in anything and returns what? A constant value. So it literally doesn't matter when you give an input to this function. So we could have saved ourselves a lot of grief if we had done the outermost beta reduction first, right? Because you do that it's just going to return Y with one step we're done. So this kind of shows even if in this example it doesn't matter which of the two choices you choose to do beta reductions you'll eventually get to the same point. This function looks like a function that just returns a constant value, right? Returns a free variable Y. What is this function? Is it duplicated? I mean it does duplicate the parameter but then what happens? It duplicates it with what? With an application so it tries to apply its input to itself which is what happens here, right? So here we have lambda W dot W. What's that? It's just an ID function. It's typically called an ID function. It's a function that takes in X that's it. Here it's W, whatever. Super simple function. So if you apply that function to itself what do you get? Itself. That's what you can see happen here, right? So we can see the self function Z applies whatever parameter it is to itself and by doing that we apply the identity function to itself which gives us just back the identity function. But then when we finally do that and apply it to a function that always returns Y then it doesn't matter what we did or why. In this example, no. In general, yes. In examples and homeworks and midterms you will have the finals for this class I guess I shouldn't say midterms maybe that scared you. Finals that you'll have for this class it will not matter which direction you go it will always be the only answer. So you can think of it, this is a good example I mean we don't, I'm not sure if I have this example but let's think about this I think this will probably illustrate this problem. So this is a function that does what applies its parameter to itself and so when you then apply this parameter to itself what do you have? It's going to return itself applied to itself and that'll return itself applied to itself so let's take this by one step so we can take this so let's see I guess I can just copy this whole thing I can get rid of this and I can say here inside here we are replacing z's with this good I think I need a closing bracket, cool and then when I do this I will get this, and what if I do this again what will I get? The same thing over and over again so you can see if I keep beta reducing this one I've gotten now into an infinite loop because I did a beta reduction and I'm basically at the same functions all these functions are alpha equivalent, the parse tree is exactly the same but then what happens if I eventually make the choice, the other choice you'll get why so yes this is why the order can matter if you're not careful because you could end up in an infinite loop here or you could just not do you want to reduce the outermost verse because it's kind of a function inside a function and when we parse argument we first parse it into the outermost function that's why I said it depends on what your parameter passing semantics are so yeah that's why by tuning that knob of which one you beta reduce that affects computation so for the examples we're going to look at it's not going to matter so that's why we'll just do full beta reduction because I think it's easier to do and it's easier to understand and if I promise you there won't be any loops and it won't matter which way you go then you have a way to self-check if you ever get into a loop you know you did something wrong yeah look at us we already just did this right so replace this with this so on and so forth you all know Boolean logic right so what do you need first yeah you need a truer or false and then what do you want to build on top of that I don't care about the gates I don't care about the higher level Boolean operators? yeah Boolean operators, you want to build ands, ors, nots nands, nors, I don't know whatever else XORs the cool thing that we do know from Circuits right is what is it, NAND is complete so if we just build one of these Boolean operators we know we can use that to build everything else with this tool but we don't necessarily need to do that we'll define all the operators here so all of our true and false functions so you kind of have to I guess I'll say how do you come up with this logic you think really hard and you try a lot of things and a lot of them will fail and at one point you will create a Boolean logic it may be similar to this I don't know if this is true or not there may be an infinite number of ways you can express Boolean operations by defining the true and false operators in different ways in lambda calculus so we're studying one way and we're going to understand that and get the intuition of why it works and to think about if you tweak things how would that change your Boolean operators but so somebody else already did this work for us so it's our job then to learn from that and think about why this person did that okay so all of our Boolean base operators true and false are going to be functions and they will be functions that take in two parameters so they will be in the form of lambda x dot lambda y so that's backwards so true is going to take in two parameters so lambda x dot lambda y and it's going to return which parameter the first one and false is going to be the opposite it's going to be a function that takes in two parameters and always returns the second parameter so if we think about what this is going to look like so we should probably walk through and see why this works so let me steal this so based on our intuition, what should this return A so let's see why it returns A so what happens first yes, we got to do it with A we have to remember this is left associated so we have first the parameter A is here so what happens so we have, we take just our mechanical steps this is why we studied all of this so we can just do this mechanically we look inside here we go inside here and we say let's say inside here replace x with A so now what do we have to check at this step of our substitution if y is equal to x or not if it was equal to x we would not go inside of it then what, it's not, so what's the other thing is y free in this land expression is it should be an easy is y free in this land expression no, there's only one variable called A but A is free so that means we can just go inside so then we can replace this x with A and so the result of this operation is lambda y dot A so what is this function lambda y dot A what does it do it's one of these functions that takes in anything and just returns a constant so when we apply this here we just take the body A and we replace y with B which is just going to be A so this is how we had the true operator taking two parameters and the remaining result from is even though each of these lambda expressions each of these abstractions only takes in one parameter when they're kind of changed like this they're simulating a function that takes in two parameters and so in this version it just returns its first parameter and we can see if we change this to y in this next step where here we replace this with A see if there is no x so this just becomes lambda y dot y which happens to be the identity function of y to B which is going to return what B so true always returns its first argument false always returns its second argument so let's define an AND function so what should our AND function do how many parameters should our AND function take two so how do we figure out what we want so we need to build a truth table just like we normally do we should know this in our heart of hearts in our minds of minds but if we don't we can always just do this we have false true false false false false so this should be input 1, parameter 1, parameter 2 output actually 40 of you or 50 of you in there right input 1, input 2, output true true true everything else false okay so this is our function so using these Boolean operations now we have to construct a function that does this but remember the truths and falses that we want to return are functions so we're going to need our AND function should be okay so we know it has to take in two parameters lambda x dot lambda y so how do we construct this logic we have two functions right so we know we don't have to worry about types really at this point because we're basically only defining our AND function in terms of the parameters are true or false so true is a function that takes in two parameters and returns the first one if it's always returns the first one and false always returns the second one it'll return the second parameter and if it's false it'll return the first one yeah so here we have x so x is now either true or false right and remember when we write like this we're doing this first right so x is a function if x is a function that takes in two parameters and so if it's true so if it's true we're in here and then so the return is going to be what of this x y x which of x of y x is going to return and so if it's true it's going to return y and so this is true and this is true the result is going to be true if x is true and y is false then the return is y which is false this is covered this and so then if x is false then what happens if x is false return itself and it doesn't matter what y is we only have to look at y we know we always return x I actually think this is a nice way to do it the way we'll look at it here is we'll replace this with just false so this becomes a little bit more clear about the logic so if x is true then the return of and if x is true then the return of and is whatever y is so if y is true then the result is true if y is false the result is false if x is false then we take the second parameter and here we're just hard coding that as false so if x is false return false if we do and true true so right now we'll just do t and t by themselves we won't expand them but we'll treat them we know they are their combinators they don't refer to any free variables so we don't have to worry about substituting them anywhere and we know that they don't have any beta reductions inside them so we can just leave them as representing this so we have and true true so if we write out the definition of and we have lambda a dot lambda b dot ab lambda x dot lambda y dot y and here I'm using different a's and b's so that we don't get confused with the a's and b's from and and the x's and y's in this false function even though these x's and y's are all self-contained so we know that they would be different just to make it easier on us we will not do that so let's walk through this, let's verify that our intuition of how to build this actually jives with what this result should return so we have this big expression we have lambda a dot lambda b ab lambda x dot lambda y dot y and then we have that applied with lambda x dot lambda y dot x and that applied to lambda x dot lambda y dot x does it look scary? peel it back layer by layer so we know that this application happens first this beta reduction because of the less left associativity of our disambiguation rules we know that first we are going to replace inside here all of this body with all the a's in here with lambda x dot lambda y dot x everyone agree? so inside here we are going to have lambda b dot ab lambda x dot lambda y dot y and inside here we are going to replace the a with lambda x dot lambda y dot x so we can do that everyone agree? so we can do that so we have replaced the a with lambda x dot lambda y dot x so now we actually do have two choices we have two beta reductions we have one in here which is b applied to lambda x dot lambda y dot x and then we have the outer lambda b with this lambda x dot lambda y dot x so we will do the outer one first it actually doesn't matter but since we have been doing the outside one as we said it won't matter but we have been doing the outside one so let's just apply this next one so inside here we will replace all three b's with lambda x dot lambda y dot x we are replacing it with this parameter here so then we replace this free b here so now we have something if you just look at this I completely agree this looks crazy you literally have a bunch of greet characters and x's and y's and maybe this is the point where you start to see the calculus inside by into calculus but that's really just a facade because we have talked about our understanding of what each of these functions does this is true this is true this is false we know true is a function that takes in two parameters and returns which one first parameter so we know that it's going to return what this first parameter it's being applied to two parameters and then we will return this parameter here we have three applications so the first application is going to happen here right let's walk through it just so that we are clear so then I look inside this body here and I say I am going to replace any free x inside this body with lambda x dot lambda y dot x with my parameter here and so do I have to worry about this y are there any free y's in what I am replacing no it's a combinator right it has only bound variables not at all so that is going to replace this x with this lambda x dot lambda y dot x so now we are at this intermediate state where we have lambda y dot lambda x dot lambda y dot x but that's fine because now we do one more application and replace inside this body all three y's with lambda x dot lambda y dot y are there any free y's in this body and we are left with this and what's this true so we just did a Boolean operation using functions and only functions and no functions with no names or sorry only functions with no names so it's basically doing true and it's taking true and also it's choosing true yes so it's using here and we're saying that the first parameter decides if the first parameter is false well here let me go back here it's a little hard to look at this if the first parameter a is false then always return constant false we don't care what b is because that's the definition of ant if it's true then the return is either true or false depending on what the second parameter is b and we'll get rid of that false and so this is why you build up the intuition behind these true and false functions so you don't have to do this logic every time but it's a nice way to check to make sure that a you actually understand how this computation should be performed because you know what the result should be the result better be true questions on this? we could have just used two t's instead of lambda x dot lambda y dot x here? yes you always have to remember that those t's can be if they're on the left side of an application they represent an abstraction so you can further beta reduce it if you had to but yeah when you're working this out on a homework problem maybe that's a good way to think about it if you're on a high level and then if you feel you need to go down to the low level totally do it this is the cool thing each of these steps is very mechanical doing these substitutions is just a mechanical operation that we wrote the rules down in four rules on a powerpoint slide but you can create these Boolean operators from that and so you do the same thing with true and false as an input kind of go over this quickly so here we can actually do it abstractly, so here we'll do it symbolically so here we have, we'll expand up the and function because that's the one we're trying to test and we'll say lambda a dot lambda b dot ad false and so here apply this, this means inside this body I need to replace all three a's with true so I would do this, replace all three a's with true so I have true b false and then now I need to replace all the b's inside here with false, so all, sorry all three b's inside the body with false I replace this b with false I say true is a function that takes in two parameters and returns which one first one and I could even expand that out further if I wanted to, I'm going to get false the witty one where it was x, y, x and then you just put a false but I think we're missing the theory as to why that works so so if a is false what is it going to return always false so that's the intuition from the table whenever a is false the first input is false and the output is false so it doesn't even matter what the second parameter is so one way to do that is to say we'll just return yourself because you know that that's false because you just checked so you're returning the second parameter because you know it's false so you're going to return yourself here to try and drive the intuition here you're hard coding it and saying if a is false always return false that is the definition of an and it does not matter what the second parameter is if the first parameter is false cool and you can do this with false and true and false and false so you just go through all of these cases and see yep the inputs and outputs match exactly what I think then this is actually done correctly so then using this how would you define not so what's the output so how many parameters is not taken one so if it's true it was a return if it's false it was a return okay so then how would you write this so now x is now a x is now the true or false that we have right so x is a function that takes in two parameters if it's true it will return the first parameter if it's false it will return the second parameter so we have x right we can go in a similar style here we need essentially we can think of this as evaluating x right yeah so x is true the first parameter is going to be a return so based on our true table what should that be false and if it's false what should we return true there we go we can write a not function and that's exactly what I have and we can prove not t we pass in t substitute that t for a true is going to return the first one which is false not false is going to return false false true and it will return the second one which will return true so using that we've actually just developed all the boolean operations we ever want right we have and and not and we can actually use those to define every single other operator cool so we'll continue on Monday with if branches and we'll get into more complicated complicated forms of computation