 Okay, thanks everyone for coming today. Everybody has the volume on the mic? Is it fine? Good? Okay, cool. So, I guess before we really start, a few housekeeping things. So, homework five was released on Monday. So that's on land to calculus. And homework six, which is the makeup homework, was released yesterday. So, any of you have any questions on how to make your homework works? I feel like it's pretty clear, but. There's always room for questions. The hand or no? No hand? Okay. So, homework six, you've said it in your message, the thread is not required. Correct, it is optional. It's a makeup homework, so. What did you get on that homework assignment? We multiplied by 70, that will be your final score. If that's greater than, it will replace your lowest score if it's greater than it, right? So, it only helped you, but if, the max you can get is 70, so it's just like the homework, the project three and project four resubmission. Yeah? The homework four is greater than you've ever done. Yeah, they should be posted. I think most of them posted it last night. At least I don't even know a man list. You know a man list, even paying attention to all year. If you have an exclamation mark, you should wait for project four. Homework four is being graded this week. So, by Friday, homework four should be graded in the end. It may even be sooner than that, though. Definitely by Friday. I thought you guys were on your project four, I was like, that's, it's not great. Okay, so, we're at right now, right? We're almost to the end of lambda calculus. So, we looked at addition, and then we saw on Monday that, hey, we can actually define, we can use addition to actually define multiplication, right? So, we can define a multiplication function and we wanted signature to take in two numbers, quote, quote, I'm using numbers because it's really functions that are representing numbers. And we want to take in two numbers and return and beta reduce to the result of multiplying those two numbers together. So, we want something that does, takes and multiplies zero and one and returns zero, right, because zero times one is zero. Zero, yay, that was a lot. Okay, multiplying one and two is two, and multiplying two and five is 10. These are the things that we want from our multiplication operator. And so, it turns out that it's actually really simple. So, simple quote, quote, in that it's not super complicated, but maybe not simple and easy to understand sense. But, we think about it, okay. So, what this is defining, so, we know that it's, this is a lambda expression that's defining abstraction that has an inner abstraction. So, we can think of this occurring, right, because this is a function that takes in two parameters, n and m, and then calls m and applies it to the function add n to zero. So, what is, so, these are numbers, right? So, what was the, how do we actually define these numbers? Like, what did the body of the number two look like? Yeah, f, f, x, yeah. So, whatever the first parameter is, we're going to apply that in that many times the second parameter. So, here in the body of the multiplication, if this m is a numeral, what's the first parameter here? If this is a number, what's the first parameter to this number? The add n, right, that whole thing. And so, what's going to happen is that is going to be applied that many times to zero. So, we have m times add n to zero, and add those all to the results. So, that's multiplication, right? So, take the second number and add n plus n plus n that many times, however many times the second number is. That's all that's in code, right? So, it actually is fairly simple. I don't know, I mean, we don't all program and then to calculate this all day, right? So, it's not like you just say, aha, I've discovered multiplication. I'll write it down exactly like this, right? Big trial and error process. The question? No. Okay, so then let's see how this actually works. So, multiplying zero and one, right? So, let's expand out the multiplication definition. So, we just use this definition that we have. Lambda n, lambda m, m add n to zero, applied to zero and one, right? So, now I want to do the first application. So, what's going to happen here? What am I going to apply? Yeah, zero to n, right? So, I'm going to, in the body of this abstraction, I'm going to replace n with zero. So, anytime every free n I see in here, I'm going to replace it with a zero. So, do I have to worry about any free variables in this zero that I'm doing this replacement? No, why not? So, zero is a function, right? It's defined as a function. So, it's not zero, zero is not the numeral we know, it is the function that we defined previously in those slides. So, why do I need to worry about a free variable in there? Or, we have to worry about renaming because of something in there. There aren't no free variables, right? The way we define zero, one, two, three, four, all of those, they're all combinators. So, they have no free variables. Therefore, we don't need to worry about doing any of that substitution and changing anything. So, we can just replace the n with a zero, right? Just like a function bomb. Combinator. Now, it would be remembers. All right, now we do the next application. So, which meta variable are we going to replace? n with what? One, yes. So, we replace that. Great, so, now, so one, we know the definition of one. So, one is a function that takes in two parameters, f and x, and applies f to x, right? So, we can apply that, right? So, this is in, this is a beta reducible expression because on the left-hand side, we have something with a lambda and we have an application here. So, we can easily do this. If we walk through those steps, we can replace it and we can add zero and zero. What's the result of adding zero and zero? Zero, yes, very good, awesome. So, this is good, right? So, this is exactly what we wanted. Now, it's looking something slightly more complicated, right? We want to multiply one and two. So, we do the same thing here. We're going to expand out and multiply and then we're going to substitute in n for one in the body there and then we're going to substitute two for n. So, we get two, add one and zero. So, this two, what's this going to do? What's the result of this going to be? Add one, add one, zero, exactly, right? That's how we define the new world two as a function. So, it's going to transform this to this, right? So, this right hand side is the equivalent of add one and zero. What's the result of adding one and zero? One, and this is the same thing as this, right? So, it's adding one plus one. Two, we've got multiplication, super awesome, right? Any questions on this? So, you can see we've skipped a lot of steps here, but if you completely expand all of these out to all of their definitions, it'll work just fine. Okay, so, are we turning complete now? So, what have we had so far? What kind of operations have we defined on land calculus? What was it? Addition. Addition, addition, multiplication. We didn't get into it, but you can define subtraction. Boolean. Boolean, so we define Booleans themselves and we also define Boolean logic operators. Also, branching statements, right? So, we define how to do an if statement. We have Boolean logic, true false branches, we have arithmetic, but what do we mean by turning completeness? What does that mean at a high level? You don't have to give like a precise definition. You can do it the way a computer can do it pretty much. Or I guess anything code could do. Can I talk about a little bit louder? Basically, we can do anything that like a typical computer could do or a basic CPU. Anything that a typical computer could do close to this special type of computer that we're talking about here. What was that? A Turing machine. Yeah, the Turing machine, right? So, a Turing complete, right? It means that a Turing machine means that any program in your language that you can write in that language, you can execute on a Turing machine. You could make a Turing machine that they could do that. Yeah. Can I defend my classmate? Yeah, absolutely. If by the church Turing thesis, anything a computer could do a Turing machine could do it wasn't the same thing. True, but the definition is more focused. I was gonna go through that back. Yes, it is, it's similar because pretty much every language that you're programming in is Turing complete. Although some languages are not. You can really think of them as programming languages per se, but something like JSON, right? It's a data standard format. It's not really Turing complete. Okay, so yeah, so code, but it's what we think of code and the reason why we think of it as code is because it is Turing complete. Yes, okay. So what are we missing? What can, can you write a function that you could write in a normal program that you can't write in, let's talk to this so far. Yes. Yeah, we need some way to loot, right? We have no way to either recurse or do a loot, right? So far we've seen we can branch, right? We can calculate, do some arithmetic. We haven't really seen any way of how to loot, right? So let's say we wanna implement the factorial function. So what is the factorial function? So n factorial, let's just define that. What's the base case? Yes. What is one? Zero equals one. Okay, yes, factorial of zero equals one, right? So guys, that was the base case. The base case is if n is zero, then factorial of zero is one. And exactly, so the factorial of any number n is n times factorial of n minus one, right? So in fact, for any n, it's n times n minus one times n minus two times n minus three times n minus four all the way up to you get to where n is zero where that is one. And then you multiply all those together and you have the factorial. Can you write this in C? Yes, probably something you did hopefully in like the first couple years of programming. All right, so then write it. So what's the first thing I do in my factorial function? If n equals zero, what? Return one. Well, return one, and then what? Then you say return n times fact of n minus two. Yeah, return n times factorial of n minus one, right? So here we have this very simple recursive definition where we wanna say, hey, call this function again, right, compute a result and then call this function again and call this function again. Let's write this in lambda calculus. So we're gonna first assume that we have functions called is zero and predicate, or sorry, predecessor not predicate for that. So what does is zero do? Let's make up semantics. That makes sense. Return zero. Are we? No. Somebody else? What was it? Return zero. Yeah, return true if zero falls otherwise, yeah. Exactly, so the function, it takes in a numeric, if we're thinking types, right, it takes in one numeric value and it returns true if that numeric value is zero, otherwise it returns false. And we can write that with the lambda calculus we already have. I didn't go into it because I don't wanna get into it. Okay, so predecessor is the opposite of successor. So what was the type of successor over the semantics of the successor function? Yeah, it takes in the number and returns the next number. So it takes in n and it returns n plus one. So what would the predecessor function do? Yeah, it takes in n and it returns n minus one, okay. That's so we're clear. These are well-defined and you can actually define these things. So let's write our function. That should be super simple, right? Okay, so we wanted to find a function to factor out factor out factor out function. And it's a function that takes in one parameter n, right, which makes sense. And then we have our if, which we define. And then we say is zero is n zero. So if it is zero, then we get to the true, which is the second parameter today of statement, which returns one, right? Otherwise, if n is not zero, then the second thing is gonna be executed, which multiplies n times factorial of the predecessor of n. And that's a little inside out of the way you're used to normally thinking about this. But if you break this down and look at it as we're thinking about Landicat this makes sense. So is this good or are we done? Go home. What's the problem with this function? Is there a problem with it? Yeah, well, we have to find meaning or assignment semantics. So what have we kind of used before? So what about, so we're using is zero here or like mult, right? So how is that different than this fact that we have here? Yeah, so remember, we defined mult as a function. We gave the exact definition, right? And so we could fully expand that definition. We're saying, hey, mult is just a place holder for this. You could take that thing and plug it in there anywhere you see mult. It makes sense. It's more of a definition in an mathematical sense, we're saying, defining mult as this thing, right? So we can do that. We can do that with this is zero function that we didn't actually write, but that easily exists. With this if, if is just lambda a dot a, I think is all it is, right? And predecessor as well. What about fact? What do we replace here for fact? That entire thing, what does that entire thing contain? If we want to replace this, this would, we have to replace this whole thing and this whole thing would have fact in it. We replace that whole thing and have a fact in it and that whole thing and have a fact in it, right? So you have this terrible, terrible. So you can't actually define this in the lambda calculus that we've been talking about, right? So you just can't write this function. We can't use the function itself in its definition because the way we're doing things, we just can't do that. Don't we give up? Come on. Okay, no, this is where other people invented, I think it was Alonzo church was when he came up with this combinator. It's called the Y combinator. It looks super weird, but it looks like this. So it itself is an application and on the left-hand side, and it's the same thing on the left-hand side and the right-hand side. So it's kind of preventing it from kind of looking at both too hard. So we have lambda x, lambda y. So this takes in two branders and it takes the second brander and calls it on x, x, y in the second brander. And then we have another application that does the exact same thing, seems strange. Yes, so let's look at this Y food, right? Let's apply this to food. So here, we're gonna expand out the Y combinator. We're gonna have the Y combinator here. We're gonna apply it to food, right? So do we have an application that we can make here? What can we apply? Anything of beta redux? Can we reduce anything? Apply food to the second section, the X one. No, because of the way the parentheses are, right? So we have the Y is in its own parentheses. So here we have a big application and inside there is an application. It's not a lambda abstraction, right? So all you need to do is apply this thing to itself in the inside. Okay, so then we apply it. So we basically, so you can see each of these is itself a combinator, right? There's no three variables in each of those applications. So it's very simple. I can take the right-hand side and I can replace every X on the left-hand side with the right-hand side, right? Just a beta reduction, which is mechanical. So I have lambda Y dot Y and then I have the left thing, the right thing, and then Y. Now can I beta reduce with foo? Yeah, because it's in the form, the left side is a lambda function, right? It's an abstraction and the right, it's a lambda function applied to something, which is an expression. So I can take foo and plug foo in here for Y. So it's gonna be foo and then this thing. And remember, I'm not gonna replace this Y because this is guarded here, right? This Y refers to this Y and I'm not gonna replace this Y because this Y refers to this Y, don't replace this Y because this Y refers to here and this Y refers to here. But I should replace foo here. Oh, sorry, but I do replace this Y. I replace this Y because this Y is bound to this meta variable here. You gotta make sure the parentheses match? Yes, okay, good. So that, this abstraction is bound, this abstraction X, Y is bound here. So this Y is bound to this Y here. Okay, so what is this thing here in the middle? What do you mean by what we started with? Ah, yeah, what, what that we started with? Yeah, it's Y foo, right? So we apply it once and now we have foo applied to the same thing. So what happens if we expand out this middle thing? We just did this, right? We're gonna expand Y foo and it's gonna give us foo applied to Y foo and we're gonna expand this Y foo and it's foo applied to Y foo and we can keep going out and out and out infinitely forever. This one here? In here. This foo replaces this Y at the front here and the Y at the back. So yeah, that's why there's a second foo here. So you can see it's basically in this form kind of where we have Y is that function and then X, X, these X's are the new Y combinator and the Y is the function again. From the definition, that's kind of how we get that. And so this will go out to infinity. So it turns out you can create recursion from this. So this is kind of crazy, but it's super awesome. Okay, this is a function we want to define, right? We want to define factorial is this thing because that's what makes sense. The problem is we can't actually define this factorial function with itself. So what we're gonna do is we're going to abstract out that function F, that factorial function and we're going to enclose this thing in a new abstraction and abstract over that function F. So that function F that we call is not yet defined. It's a parameter to this abstraction and then we're gonna enclose that whole thing with the Y combinator. So let's run through an example to see what happens. Okay, we do factorial one and this is actually incredibly complicated so I'm gonna go through one example. Okay, so we just substituted one here. So we have the Y combinator and then we have the definition of our function that we want factorial and then we have the parameter of one, right? So I'm gonna apply the left, I'm gonna do a beta reduction on the left and the left side first. So here, actually first I'm gonna expand, okay, yes. So we're actually not gonna go through the sets and expand to the Y combinator because that is crazy. So what we're gonna do is we're gonna use what we've learned before with that Y foo that's gonna beta reduce to foo, Y foo, right? So here foo is just this thing, right? So we just treat it symbolically. We take that and replace it. So we say, okay, well that same thing again applied to Y, that same thing, one. Okay, now we need to do our first beta reduction. So what can I beta reduce here? Yeah, this whole thing Y lambda F, right? This whole thing on the right here is being applied to this lambda F, lambda N. So I'm gonna just take this whole thing and replace F in the left-hand side with that whole thing, right? And this thing, remember, is the thing I originally had, it's Y, YR function. So I'm gonna replace F with that. And so you see this F here, which is where we wanted the factorial, gets now our Y combinator applied to that function that we just had. So we're gonna do that step and we replace this F with the Y combinator. So now we have a big long thing that we can actually, and we got rid of the F, so now we can actually apply this to one, which is what we originally had. So we apply this to one, we substitute everywhere this N is bound here, we're gonna substitute in one. So that's this N here in the inside zero check, which makes sense, and it's this N in this multiply here. But notice it's not gonna touch the N in here because this N is bound to a different abstraction, right? Let's do that, we replace that. So now we have if is zero one, return one, otherwise multiply one by that same thing with the predicate of one. I forgot about this N here at the end, right? With the predicate of one. Cool, so now I can evaluate whichever one I want now. So let's say, let's do is zero one, what's that gonna return? False, exactly false. So then I have if false one this whole thing, what's that gonna return? The what? The second thing, yeah, so it's gonna return this, so it's gonna return multiply one by this whole thing applied to the predicate of one. So what is this guy? Predicate of one is zero, we're gonna get to it later because I forgot about it, but I'll assume these steps. But it's a point that it doesn't matter what way you reduce them. But, right, we can reduce the middle here and we've already done this. We can expand that out and now we have F of N if size zero N one, multiply N by F predicate of N and then the same thing, right, Y of two. So we have Y combinator applied to two again with the predicate of one. Now we can apply that F in the function just like we did before. So that F is now this Y applied to food. Then we do the predicate of one because this is definitely where it makes sense to do that. Now we can apply this zero to this N, right? We can substitute in this N. We substitute the N for zero and we have multiply one by if is zero zero one. Otherwise, do this other thing. And so what's is zero zero gonna return? True. And so what's this gonna return? The if one, multiply one by one. What is that? One. Yay, we just calculated the factorial of one in a super crazy way. You wanna do it again? Factorial of 10? All the way down to no. We will not, but it goes to show you that you get this recursion from this Y combinator. So we basically parameterize the function that we wanted to call, recursively call and this Y combinator allows us to call a function essentially with itself. So we can actually do recursion without having any named variables or any named functions. Which is pretty cool. So we have, we've now defined it, I'm definitely not gonna go into the proof. We defined a term complete language here using only functions and only function application. Those are the only two things. We defined Boolean logic. We defined arithmetic. We defined multiplication. And then we defined basically loops. Because recursion is equivalent to a loop. Is that insane? Yes. Yes, it's insane, it's crazy. Do I love this guy? Okay, any questions? Why would anyone want to use this? No artifacts. What do you mean no artifacts? They're like, you don't have a, Yeah, so one of the things, right? I mean, you gotta think about compared to Turing machines, right? So that, you know, you're not gonna program this. It's like you're not gonna program a Turing machine. But the question kind of is, what's the most simplest form of computation? Right? And to me, this, I mean, there's no variables. There's no infinite take. There's no, this machine that can move left or right. All this is function application. And yet it's just as expressive as Turing machines. And just as expressive as your C program or your Perl program or your Python code. This logic isn't valuable if we care about it. Right, keep going, I had, keep going, good. And that's depends on that. Okay, keep going, I wanna see where you're going. I don't know if I agree with that and say no premise stuff. Would Y combinators be, Y combinators must exist if recursion exists? Okay, so if I tell you that they're, that, wait a second, so. You couldn't have a universe in which you had something that's recursive without Y combinator and logic. Or Y combinator and logic without, I mean, they are necessarily the same thing. Short answer is probably I don't know. The longer guess is you have to have that. So one of those things is like a chicken and egg thing, right, it's like if I tell you that this is a Turing complete language, then you must have some way to loop for recurs, right? So if I tell you that, it's up to you to find it. But this isn't how these things started, right? So they started with Turing creating these Turing machines and church and all these other people developing landing calculus. And then at some point they were fighting over, well, what's the actual true way to, what does it mean to compute something, right? Is it something that's computable on a Turing machine or is it something that's computable with landing calculus? And then they kind of prove that they're actually equivalent. So they probably use these properties to prove equivalence is my guess. But it's more like it came in that direction, right? If you have a language that you can do logic or arithmetic and unbounded loops. So they popped up in different areas and then just happened to be. Yeah, that's crazy, right? Yeah, yeah. My intuition is similar but harder. Hopefully how it was. Oh, thank you. I have, if you work hard enough on it, I'm sure you could find the vision here. Yeah, everything you could do back for, I mean, fractions, you could do exponentiation, do derivative, maybe, I don't know, maybe getting out of my depth over claiming things. Any other questions on the subject? Okay, so that kind of actually wraps up the class here, which is super weird to say. It's been a short but fun 15 weeks. So I kind of want to go back a little bit because I'm thinking a lot about this class and hopefully you're not sitting there going like, man, I've wasted 15 weeks learning about junk that I'm gonna forget about for the rest of my career. Oh no, nervous giggles because we had those thoughts. Oh, the more I think about it, more and more. Okay, your problem, so. Leveling with you, yes, you're probably not gonna be writing a compiler in your life. Like a legit, taken in brand new programming language that you created and spit out x86 code. It's possible, so I hope, part of the reason why you should take this class and the thing you should take away is that it's possible, right? You can do it. You can write your own crazy programming language and write a compiler for it. You've written, I don't even know how many parsers now, right? You're pretty good at parsing, I hope. You understand how to loop over the data structures and how to create intermediate representation, right? These are not things that only programming gods do, right? This is basic bread and butter computer science stuff. So that should never deter you in the future. The other thing is these things come up all the time in places where we do not call them compilers. So if you're ever parsing something, parsing some input, right? You're essentially building a tiny little compiler using stuff you learned in this class. Like what does a web browser do? Yes, it takes bytes from a server and parses those bytes into HTML into a graphical representation to display to you, the user. It is legit, even just not thinking about JavaScript or anything. It is still a compiler. It's taking random raw bytes and trying to make sense of it. And that's using the stuff that we learned in this class. Those cases exist all the time. Even data formats, right? Reading a JPEG in it, reading in all these kinds of stuff. You're essentially parsing something. So I find it highly unlikely that you know your entire career with that, however having to parse some weird data format. So hopefully you realize, well, maybe these are kind of useful. Hopefully the projects help to become a better programmer. That's also a sub-goal to this class. Cool, okay, with that we'll go to midterms. So, thanks. All right, first thing that everyone wants to know, midterm stats. The median was 84, the average was an 81, the standard deviation was 13. The minimum is 41, the max is 100. You wanna walk through the midterm? Oh, yeah, the back, yes. So I know this, for the two midterms, it's 25% of overall grade. Yes. Does that mean they're both equivalent? Yes. Half of that, okay. Yes, okay. Look at the midterm. Okay, this is for version one. So we'll go over this. You should be able to figure it out based on your picture. So, if you're not, version one, it should be very clear. Okay. Global variables are allocated on the stack. Oh, and when I say we go over it, I mean, I'm not gonna just tell you the answers, you have to tell me the answers. So, global variables are allocated on the stack. False, where are global variables allocated? It doesn't matter, not on the stack. Okay, good. In the sedeco calling convention, the colleague creates space on the stack for local variables. True, right? And it makes sense, right? You actually don't want the caller to create this space because the caller is just calling some function. It shouldn't have to care how much space it needs for local variables, right? Okay. In the sedeco calling convention, the colleague pushes the arguments onto the stack. False. False, right? By the same reasoning, how are you, you're the function that got called, how are you supposed to push variables onto the stack, of your parameters onto the stack, right? The arguments come from the person who calls you, so they definitely should be pushing things onto the stack. You guys, questions? There's a good question about these, by the way. There's your hands, don't normal class them. Okay, on the x86 architecture, all memory addresses on the stack below the stack pointer are garbage. Depends on which way you look, not really. Below, I think, has a very specific meaning. And the true or false? True. True? Why is it false? Why false? Yeah? Because the heap is underneath. Because the heap is underneath. So this is a poorly-worded question and one of your fellow students alerted me to that fact on the writing, so you can thank them. So yeah, except these are true or false on this question. So, is poorly-worded? It was intended to be true. So, the intent was to be true, but I realized I was too specific by specifying x86, because the heap is below the stack, then it is also false as well. So, the intent was true, but I accepted it all. But it says it's not on the stack. It's not underneath, it's not on the stack. No, it says it's all, but it also says it's all memory addresses. So, also below the stack, it doesn't make sense there. The stack is only defined as from the top to the current bottom, to DSP, right? So below it also doesn't make sense either, so. Poorly-worded in two ways. Thanks for pointing that out. Heap allocation is automatically deallocated in C. False S, right? Heap allocation, if you call new, you have to call free. X is equal to y in sharing semantics means to bind the name X to the location associated with Y. True, right? So here, this is definitions of sharing semantics. Okay, anonymous types do not have a name. Exactly, they are anonymous, they do not have a name. The compiler may give them an internal name, but they do not have a name. Yeah, but that's not its name. It's an internal name that's only relevant to a compiler. I made that very clear when we went over the, over the practice midterm. Anonymous types do not have names, that's why they're not named with one. A dangling reference is memory that has been allocated on the heap has not been explicitly deallocated, yet is inaccessible by the program. False, what is it? What is it? Garbage, yeah, garbage memory is memory that's been allocated on the heap has not been explicitly deallocated and is inaccessible by the program. What is a dangling reference? A dangling reference is when you have a reference to memory that has been freed already. So here the big difference is, Oh, you also have a pointer default. Right, exactly, yeah. So if you, it'd be like returning, yeah, if you freed a variable and you still have a reference to that memory, that's memory, that is now a dangling reference because the area that that memory points to is free, has been explicitly deallocated. Programmers define new types of type constructors. Yes, you have to do that right, type constructors. Okay, in Linux Ritsi, malloc allocates new heap memory for the program by itself. False, what does it call? Something within the S, which is why every side of this, yeah, S break, S break, it calls S break. Okay, question on these? No, good try, I think it was pretty good. I agree, but it is clear from the context of the class. Explicitly, because we covered that for two days, so. Okay, problem two, dysfunction. A, B, C, D, if C, D, then three plus four, three point one, four, plus D, array access A, 10, D, L, B, C, function called D, zero. Okay, so one thing that came up during the exam, there should be no ambiguity over what this means, right? Because you have the syntax tree, right? So if you're curious about what this meant, all you have to look is here and say, oh, it's a function call where the left hand side is the result of calling B, giving it the parameter C, and then calling that with the result of D, or array access zero, yes? On the final, will we have to worry about anything? Yeah, you have to do it on homework six, because I don't want to draw it, but. I'll, yes, I'll do it for the final. Okay, what, what, what, yeah? To be like an event coverage or everything, that's for that? Anything, anything, any distribution that I feel like is appropriate? 90% of that would be like, I don't know, should be anything. Anything is fair game. Oh no, we have to remember first and follow-sets again. Okay, so, okay, first thing, fill in the blank, reduce the vote, important thing, reducing all types to basic types and type constructors is possible, right? So no type A, type D, type C, type D, right? Those should all be reduced now. Okay, ABCD and the type that have returns. Okay, so we can go through this quickly. An easy thing, so we know, okay, an if branch means that, so whatever the if branch returns is what F returns, right? So we know that the plus branch and this branch have to return the same thing and so we know that this thing is 3.4 plus something, so this is a real, this returns a real, the addition of two reals returns a real, if this branch returns a real, that means this branch also has to return a real, which means that F has to return a real. This is actually too difficult for me to just talk about, so let's go to the key, to the key. Okay, so A is a function that takes in two parameters, one in int, one in array of reals and returns an int. So if we look at A, we can see A here is a function, it takes in two ints, so, no, it takes in an int and D, what's this, B, which is an array of reals and returns an integer, which makes sense, right? It has to return an integer because it's being used in the array access operations, so it's got to be an integer, okay. This one was cool, B was cool. So B is a function, if you look here, right? It's a function that takes in something of type C and whatever it returns, it has to be a function, right? And we know it has to be a function because it's being used on the left-hand side of a call operation. So just like land vocabulary says functions that return functions, right? This is exactly this, it's a function that takes in, oh, C, it takes in an array of reals and it returns, oh, no, it's a function that takes in an array of reals and returns a Boolean and it returns a function that takes in a real and returns a real. So this is a function that takes in a real and returns a real, yes, because D is an array of reals. And C is a function that takes in an array of reals and returns a Boolean. C array of reals, Boolean, D array of reals, F returns real. So I don't know why, I try to make it easier by not putting the type of F, right, just the type that F returns, and yet some people still put F type here. So you don't lose a ton of points, but reading is important. Okay, any questions on here? So it doesn't matter how complicated these things are, functions are turning functions are turning functions, right, you just apply the same rules over and over, right, and that's what it goes. Any questions on this? Oh, research graph. Okay, problem three, for each of the following types, list all the types that are structurally equivalent, okay. So the first one is A, A is a string, is anything else a string here? No, so, and remember, you said list all the types, so you should definitely list A itself, right, so A is structurally equivalent to A, okay. E, E is a structure containing one field of food which is a string, the next field barge is an int, is, so it's definitely not structurally equivalent to anything before. It's not structurally equivalent to this, because this is a pointer, this is a pointer, this is a pointer and a function, a pointer and a function, so all of these, it can't be structurally equivalent, the only thing it can possibly be equivalent to is F, so is it structurally equivalent to F? Why not? Order, yes, because the order is important on structural equivalents, right, not the name, okay. G, G is a pointer to H and a C, where C is a pointer to A, where A is a string, so C is a pointer to a string, so we know this first one is a pointer, this is a pointer, but this is a function, so we know it's definitely not equivalent to G and I, we look at G and we say, okay, it's definitely not equivalent to J, because this is a function and we know that C is a pointer to a string, so it's not equivalent there, it's not equivalent to anything else, the only thing you're possibly equivalent to is H, right, so we assume that they're equivalent at first, right, and then we check, okay, is C equivalent to a pointer to a string, yes, C is a pointer to an A, A is a string, so C is a pointer to a string, which is structurally equivalent to a pointer to a string, right, so that's one, the other thing is a pointer to H, so X is type D, D is type pointer to H, is a pointer to H, structurally equivalent to a pointer to H, yes, so G and H are structurally equivalent, now I, so we already kind of showed I's not equivalent to anything else, the only thing that could possibly be equivalent to is J, so the first field is a pointer to J and the first field is a pointer to I, are these structurally equivalent, yes, because we assume that I and J are structurally equivalent at first, then we have to look at the next field, right, so there are two functions, do these functions have the same number of parameters, yes, do they have the same return type, yes, now we check if they're parameters are structurally equivalent, is J structurally equivalent to I, yes, we're assuming it is, is B structurally equivalent to a pointer to an H, let's look at B, sorry, B is a pointer to an H, yes, so I is structurally equivalent to J, any questions on those, okay, here we have fun times, a bunch of definitions, W, X are strings, Y is an A, Z is a C, P and Q are arrays of I's, R is array of I, S is a function that takes in an I in a consonant, T is a function or T is an I, so for each of the following statements, indicate if it's valid under name equivalents for I, structurally equivalent for II, internal name equivalents for III, otherwise write IV, write everything that applies, so P is equal to Q, is that name equivalents, no, why not, they're not on the side so they don't have a name, exactly, okay, are they internal name equivalents, yes, because they're defined in the same line by the compiler, so the compiler will give them the same internal name, are they, sorry, on a structurally equivalent, yes, okay, so 323, Z, how are we going to know the time, how are we going to appreciate it, Z is equal to Q zero, so Q is what, array of I, what's Q zero going to return, an I, and what is C, Z is a C, which is a pointer to a string and I is the structure, okay, so is that name equivalent, C and I are definitely not the same name, so that's where we can go off, is it structurally equivalent, no, is it internal name equivalent, nope, so we write IV, X and Y, okay, X and Y, so X is a type string, Y is type A, is it valid under name equivalents, no, because they do not have the same name, is it valid under structurally equivalents, yes, because they're staying structurally, is it valid under internal name equivalents, no, because one's a C and one's a string, all right, WX is name equivalent, structurally equivalent and internal name equivalent, right, they're both strings, Q and R, Q and R, array of I, array of I, so is it name equivalent, no, because it doesn't have a name, they're not in this type, is it structurally equivalent, yes, are they internal name equivalent, no, they're Q and R, all right, defined on different lines, so they have different internal names, okay, this one, this guy, okay, Q is an array of I's, so if you take Q and you do the array access operator, what does that, type does that return, take I, okay, great, then S is a type, takes in an I and returns an it, so S takes in a type I, so is that called valid, yeah, so it returns what type, an it, and then here we have an array access of P, which is an array of I, and we have access with an integer, so what's that going to return, an I, exactly, okay, T is an I, so is it name equivalent, yes, right, the types here are I and I, right, it doesn't matter how we got to that type I, that it was through these array accesses or function calls, right, it's the only thing we care about is comparing the types, okay, is it structure equivalent, yes, name equivalent, or internal name equivalent also, good, question on that, all right, yes, could be right, why'd you get my points introduced up, you should come forward, okay, all right, consider the following code in C syntax, they're gonna quote on paint to this thing, that's a whiteboard, but it's not gonna record if I do it on the whiteboard, we'll use PowerPoint, okay, so let's first look at this question as asking us to do, so consider the following code in C syntax, generic X, we have program main, so let's draw the program stack and have the first execution of location one, label on the stack all function frames and excited function frame label the parameters of the function, the value of those parameters that function local variables and the values of those local variables, you don't need to follow precise C-deck old calling convention, assume static scoping and pass by value standards, okay, live PowerPoint slightly, okay, so here we're starting off executing main, main has one parameter I, so what's the value of I first? So what's the value of I when we first execute this? That doesn't matter, right, 10, 100, 300, 500, we don't know, right, so this is main, we have variable I, sorry, this is gonna be really ugly because I didn't really think this through, all right, close up, okay, I, it has some unknown value, so that's the parameter of main, main has a local variable baz, which has a value and then it has a care pointer C, which has values one, zero, one, and two, right, so all of this is main, so that's all main, okay, first thing that happens, if X is equal to 10, X is this global X, X starts off equal to 10, probably that, then it says I to be 20, I is 20, and then it says X to be zero, so now X is zero, and then it prints out, is that relevant, no, it's not relevant, okay, prints out I next, okay, next thing, if I is greater than zero, is I greater than zero? Yes, that's the value 20, just gotta go to that, then call bar and pass in C to zero and one, let's look at a function bar, it has the parameter A, parameter B, parameter C, and so A has C two, C zero one two, which is two, C zero is zero, and C, C is I, which is 20, okay, and we have a local X and a local Y, and then the first thing we do is set X equal to C plus one, okay, now, important things, this function call, what happens first? Boo, right, we're calling the function foo, and we're passing the result to bar, so we have to go create a function for foo, and what are we passing in here, so in function foo, we have alpha as a parameter, we have beta, and then alpha is C, which we know is 20, and beta is B, which we know is zero, and then internally it has its own internal A, first thing foo does is set A to 10, and then it sets X is equal to X minus 20, X was zero, now it's negative 20, and then what are we returning? The result of calling main X, right, so we really, we need to create another function frame for main, which has a parameter I, which has the value X, what's the value of X? Minus 20, so we go back to main, we say minus 20, it has, we have a new baz which doesn't have a value, we have a new character C, character AC, zero, one, two, and then we say it's X equal to 10, nope, it's negative 20, is I greater than zero? Nope, I is negative 20, and then so we go to location one, and we stop, and this is our stack here, so we have main bar foo main, questions on that? Okay, I'll show you the answers first, and then we can talk about what they are. Okay, so here's a function, similar ish, but different, or no, that was similar, okay. Use static scoping, and basically you're asked if it's passed by value of the output, if it's passed by reference, if it's passed by name, what's the output? So, if it's passed by value, this is the output, zero 42, which happens here in the function foo, A is output, the value of A is zero from right here, star B is 42, the next thing that's output is C, zero, one, and two, remember this is passed by value, so they don't change zero, one, two, and then we print out 10 and 20 here, 10 is from here, and 20 is from here. So why don't this Y change? Yeah, it's passed by value, right, so, what, so we malloc a new memory address, and we copy that memory address, and we put it in the value for Y on this assignment statement, right? So this is exactly what we've been talking about for assignment semantics for pointers, right? So how to box the circle in there is the result of this malloc call. Then we set star Y, which is what Y points to, we're gonna set that to be 20. Then we call foo, so what happens to B here in foo, passed by value. We create a new, so it's passed by value, right? So B gets its own box with a circle, and we copy the value that's in Y to the value in B. So B, now the value in it is that address of that thing we just malloc, right? So when we say B is equal to malloc new size of an integer, right? Malloc creates a new box for us, and it takes that address and puts it in the value of B, so now B points, has a memory address, it points to this new malloc memory location. But did that change Y? No, because they're completely different, right? The values are different. And now we change star B, right? We're changing this new B from this new memory malloc. And then this doesn't change anything, I'm passing that out. This is why we get this output, yeah? How much statement was it that in the function it just puts down B is equal to some of the value? Would that change? Yes, so that's the thing, right? Pass by value is you can't change what you pass in, right? The whole point is you can, so in pass by value you can never change what Y, the value that's inside Y. Now, because what you're copying in is an address with the star operator, you can change what Y points do, but you can't change Y itself in pass by value. So yeah, this hopefully, pointers are not special, right? They're not any different than any other bad side. You've learned everything you need to be able to do this, right? So we've done pass by value, we've done mallocs, we've done box circle diagrams, all that stuff. Okay, have you passed by reference? The thing that changes is the last thing, changes to 042, because in this case, I believe it's X is changing in pass by reference. And then if you pass by name, now, when you pass by name, it's the same as pass by reference, except that foo, this array of C changes based on this here in bar, right? So because this expression is CI, and this I is changing inside bar, that means that this array is gonna change. So we've got 042, 011, 042. Any questions on the midterm? Got about a minute. All right, thanks everyone, see you on Monday.