 Let's start with the third lecture. First of all, one announcement for those of you who are not subscribed to Piazza yet. You should be. The 1PM discussion tomorrow is going to move to a different building. And I don't have the room here, which is a great opportunity for you to find out how to log in to Piazza and read the announcements, because, yes, you, I expected to. So the next time you don't tell us, oh, I didn't know we need to submit the partner. So whatever shows up as an announcement on Piazza, you need to know. So for most of you, that was the first mashup that you wrote in a few lines of code, probably more hours than lines of code as it happens with new tools. And that's part of the lesson here, that modern languages are, modern programs are written in multiple languages, because each of them is tailored to a task. Rejects do something well. JavaScript does something else well. And so the benefits are there, but the learning curve is still very steep. So that's part of what you want to think about, how to design your final project so that it shows how to take a problem of the sort you encounter during the homework and make things easier, easier to learn, easier to use, easier to debug, anything of that sort. So the first programming assignment, which you will be working on in pairs, has been assigned today. Today we'll cover the stuff that you'll do in the assignment, and more will be practiced in the discussions tomorrow. So go there and listen carefully, ask questions. You have a chance to at least read the handout, download the starter kit, look at what needs to be done so that tomorrow you can ask the hard questions. You will be working through Bitbucket, which is a repository for version control distributed, all stored on the server. And we want you to actually exchange code through Bitbucket and not email. I know it may be easier to just do email rather than learn how to do it, but we won't let you do it because we want to see how actually after you download the starter kit it goes from one laptop to the next, and both of you can actually get the code and run it. So there will be a little bit of incentive on our part to make you use Bitbucket. You, well, you can do whatever you want as long as we can check it out with Mercurial because that's how we'll actually get your submissions. So probably not my guess, right? The codes are small enough that you can use Mercurial. It will be more than enough. Okay, so what we'll do today. Remember what we did last time? Last time we essentially refreshed the metacircular evaluator from 61A, how to write interpreters. We didn't do it on building a little scheme interpreter, but instead we built a unit calculator model after the Google calculator. So we didn't just evaluate the expressions, we actually converted the units both during evaluation and during printing. There are no function calls as we have in scheme, but otherwise there are many things that made the task more interesting. What we'll do today is extend that language so that the user can actually add more units to it, essentially make it extensible to the user without touching the interpreter. And that's sort of to close the lecture from last time. And then we'll get into the lambda interpreter and again look at what features it needs to have so that people can build new constructs on top of it, again without touching the evaluator. So essentially building a bigger language without touching the compiler, you could say. So the calculator language. So remember what we have done last time? We had units, we could convert them into some other units just before printing. We would separately evaluate the left part and the right part and do some conversion at the end. Essentially we match the functionality of the Google calculator. What we want to do today is allow the user to add custom units. So for example, imagine that you want the user to have the ability to add units such as light years or feet given that the SI unit meter is already known to the interpreter. So these two you want to give the user the freedom to add without actually opening the Python definition where all these non-SI units were defined. So how do we do that? How do we give the user to the ability to add such units? So we want to make the language extensible in the sense that we want to add some constructs in the language so that the user can extend then the language further. So we need some features for extensions. So what would we add? OK, so essentially you are saying let's have a command of some sort which will have a new unit, say light year. And it will be some number of meters, whatever that number is. They should know, but I don't. And somehow it binds these two together, right? It says that this here, this new name, is bound to this value. This is our name. And this is our value after evaluation. And we want to create this binding. So what constructs do we use? We could invent any syntax. We could say add new unit. But what I'll do I'll just create something that looks like assignment. So it's an assignment which has an ID identifier, a new name on the left-hand side, and it has some expression on the right-hand side. We evaluate the expression and bound this to this name. So how will we implement it? You even hinted at the implementation of the construct. If you remember what we did last time, we had a big map in our interpreter. The map was mapping something to something. You remember what was the key in the map? Key was the unit, for example, a foot, right? And the value here was essentially foot normalized to the SI unit. So in this case, it would be something like 0.3 comma. And we had another map here which would say it's in meter to the first. And we had a bunch of these units in there. And the SI units appeared on the right-hand side in the values. And when we encountered something like FT in the evaluation, we would go to this map, look it up, and after that everything would be in SI units. So the question now is how do we implement the new construct? Previously, we had all these units hard-coded in the table. Now the extension is trivial. After you evaluate the right-hand side of this assignment here, you just put the value in here, you put this string in here, you grow that map, and you are done. So the extension is very simple for both the user as well as the implementation. We just make the table extensible using this new construct which defines a new name. So this is just to remember what the interpreter was like. Well, so this is how you could use it. You have seconds in your interpreter. Now you can define minutes, hours, days. You can even define a month, which of course is 30 and a half days, and so on and so on. So now we want to extend it further so that the user can actually define not only new units like light years, which is a unit of length. But we want to define measures that don't exist in the interpreter so far. So we want now a custom measure. So it's not a problem to define something like a unit of energy because it's just a joule. You can express it in kilograms, meters, and second square. But if you want something else like the unit of current, which you cannot break down, or if you want to define currency with, say, the Big Mac as the SI unit, or the unit of coolness, which apparently has a standard unit according to Wikipedia, you cannot quite do it. So do you see the problem? Do you see why we cannot, using the approach that we have so far, define new measures? Where will the interpreter get stuck? When we evaluate the right-hand side, it will get stuck. So the problem is right here. So imagine you want to define 1 milliampere, so 1,000th of the unit of current. On the right-hand side, we do not know the value of A. So it will presumably throw an error that this is an unknown unit. So how do we solve our problem? OK, exactly. So I'm not sure I entirely agree with the solution, but let's see how we would implement it. So we see an unknown A. So we would add into the table A for ampere, and it would be mapped to 1A is mapped to A to the first. That's what we want. So it's simple. There is a little problem with that, and it's sort of a usability problem. Can you guess what problems the programmer's users could get into? If we misspell A or something else, all of a sudden it's entered into the dictionary, and you may not notice it, because there is no diagnostics. So how would we extend this a little bit? The implementation is correct. I just want to prevent the mistakes of misspelling without noticing. When we want to introduce a new measure or dimension, we'll say, this is a new SI unit. We'll do something like SI space A, which declares it. And now, when we use A, it better be declared before, either as an SI unit or some other unit. So we'll add this declaration. And now you can define any derived units after that. That's it. So it's essentially as before, except there is a little bit of safety built in. So this is the solution. Now we can define ampere, big max, and other currencies defined from that. And no magic. We implemented it actually already. So now let's look at the reuse of values. I'll give you a scenario that may be interesting. So we want to compute the number of power bars built during a half an hour run. So you introduce, now we're actually programming this entire interpreter from scratch. We do not need to have any pre-baked units. The interpreter is now so extensible that not even SI units need to be put in. So you can think of what I'm typing as some configuration file that is read when we start the interpreter. So we define meter kilogram second, now the pound, the Newton, and Joel, calories, and so on. It all can be done by the user rather than the provider of the language. And now the power bar is nothing but another unit which is 250 calories. Isn't that nice? So now we go to Wikipedia and find this formula for how much energy we burned during a run. So here is your weight. Here is some magic number, which I have no clue how they determined, but probably they measured it on people. And we run for half an hour. And we print it in power bars. We can do it because power bar now is just a unit. So you don't need to print it in joules or calories and calculated. Power bar now has become a true unit in our calculator. So here is the answer. Now imagine that you go for another run and you clearly don't want to go to Wikipedia and remember this formula again. And you don't want to retype it also, right? So what would we do? Well, we could define constants so that some part of the formula is remembered forever. So what would we put into our constant? Magnitude and the units. In fact, for me, I would put the whole thing here since the weight is relatively constant. So this I would put into a constant, all right? So how do we implement this? OK, so the first half you're saying is before as we introduce new units. The second half, why do we need more entries in that dictionary? I see. OK, so you are probably doing it really nicely, saying the constant is a number and the unit comes separately. But we could also decide that the constant comes with a unit. In fact, gravity is a constant that comes with a unit, right? So I think it makes sense actually to make these constants to have a unit. And so what we'll do, we'll just define it like this, right? C will be this constant. It has its unit. That's fine. And one morning, we type in this and it gives us the value. Not in power bars. For that, we would have to add in power bars here. You can do it if you want to. And the next morning, we run longer, so we type a different number. So is this going to work? Are we going to get the correct answer? This is in minutes. This is in hours. What do we need to do to take care of that? So essentially, the proposal is to somehow normalize the C. So the question, let's see whether we've been paying attention. So who thinks that we need to do something special to take care of the fact that I want to use units in these two different ways? Who thinks we need to do nothing and it just keeps working? Yeah, it will just keep working, because after all, this is an hour is normalized to whatever unit, a second. And minutes are normalized as well, so it will work just fine. So indeed, to support constants, we don't need any extension because we can use the support for introducing new units like feet and light here. OK, so now things get a little bit more interesting. We want to reuse expressions. Now, what do I mean by that? So imagine that now, which is a variable that I could set to this date, if you, what date does it actually describe? Well, it's OK, it's saying January 18th, 2012. So is it 18 or is it actually 19? January 1st would be zero days. So we don't have quite good support for entering dates. I'll give you that. So it's a funny way to enter dates like that. But let's go along and try to push on this example. And I can define now as a current date, just like you see over there. But imagine I do better. Imagine that I create a library function that runs every millisecond and sets now to the current date. So imagine now, wherever you print it, you print it now. You print it tomorrow. It always gives you the current date. It's easy to imagine. And now we want to figure out how much time we have left to the deadline of, say, the assignment. So we set the deadline as a date in a similar way. We'll subtract these two times, one and the other, and we print the value in days, as opposed to seconds. And we get how much time is left. And now you would like to do the same in two days to figure out how much time you still have left. So the beauty of the design we had before, that if we wanted to support constants, we didn't have to change anything, just the way of defining our units worked for constants as well. Are we going to get as lucky here, or do we need to make some change? So what change do we need to make? Or first of all, why this will not have the semantics that I have in mind? Somebody in the back to wake up the top row. That will work. So time left is essentially a name or a variable that is bound to a value. What value? Well, the value, the result of that calculation. This is how we define the semantics. We see an expression on the right-hand side, be evaluated, and everything is bound to that value. And therefore, time left is not going to be re-evaluated. So if we want to re-evaluate, what do we bind this variable, time left to? We want to bind time left to something else, not to a value, but something else. We want to bind it to expression. Which expression do we want to bind it to? So we're going to bind time left to this expression. And let's see. It will be this expression here. And in fact, it will be then this expression. And it will be also bound to now, which is updated in the library. So now the mapping is not from a name to a value. It's named to an expression. And the expression is evaluated when? Now we have some choices. Can we hear two choices on when to evaluate, re-evaluate, time left? Each time you print its value. So you would do it sort of on demand, just when the value is near. And the other choice, maybe the dumber choice. This one is great. You have to do it at least that often. Every time now changes, you could just push the value out. And that would have the benefit that if the computation is expensive, you will have the value ready each time you need it. Imagine network communication is involved. You may actually want to push the value now through it. But that's essentially it. So what we have done is build the lazy evaluation that binds names to expressions. It could be functions in general. And we evaluate them as needed. So we are using not the value that we stored before, but entire expressions. Certain things become easier. Now, can we do it with a small change to the interpreter? Can we just extend something? What do you think would be involved in the interpreter? Because I want people to think a little. OK? You could just store either like an expression or a number. That needs to be evaluated whenever you agree with it. So we could change the language such that we have support for both lazy and eager evaluation. And then somehow ask the user delay this for later. That might be painful. So it might be probably easier to just decide in the language the entire language is eager, evaluates as soon as it sees an expression, or the entire language is lazy. So I would say if you want to make it non-confusing, perhaps we should just decide that everything is lazily evaluated. And the values, the names actually store expressions rather than values. But for that, we would need to change the interpreter and the table. The map would not map names to values. It would name them to expressions. And those would be evaluated at the time of printing. So what have we got? So the calculator is an extensible language. We added constructs, the assignment, and the SI to help the user grow the language without actually knowing how the implementation looks like inside. And as a result, they can introduce new units, new measures, keep constants, and so on. And all of it by doing actually very little, but doing it judiciously. There are some limitations that you may want to overcome. We don't have, for example, relational definitions. I cannot write this equation and solve it. There was a proposal to do that. We decided not to do it. So this is not supported. We are not solving equations. Although in the prologue interpreter that we will build, this will be possible. And finally, the parser that is distributed with the code which we expect you to look at is very simple. It does not distinguish between this parenthesis here, sorry, this division operator, and say, this division operator. In fact, if you look at this expression, there are maybe three kinds of division operators. You could say there is the one that defines one half. Here is one that divides a number in units. And here is the one that's inside the unit. The parser does not do it. This is a little bit tricky. And we need a little bit better parser architecture than what we are giving you. You will build such a parser yourself. But simple example, you saw how to grow a language, how to give constructs that you can grow it. And we also learned that grammars are a convenient way to describe the programs you can write, what constructs you have. Not quite their semantics, but at least summarize the constructs. So now we can switch to, actually, real functional language with which we can write real programs. So what we're going to do, in order to write real programs, we want to support real abstractions. Real abstractions are things that hide implementation details from you, so that with a lot of detail underneath, you can build much bigger programs, compose them nicely, without getting completely confused by what's hidden inside the interpreter. And we'll build control abstractions today. These are abstractions that decide how the program runs. What I mean by that? Can anybody say what is an example of a control abstraction? A forlub would be one. A forlub hides a lot of plumbing on how to increment the induction variable, or how to decide whether to do one more iteration or not. So mostly it's going to be a review of 61A. But again, Sprinkel did something more interesting. And we'll develop a language, and I'll guide you through a few mistakes. And then we'll pay through these mistakes later. And they essentially already play mistakes that happen in the past. And hopefully we'll learn from them something. We'll also talk about syntactic sugar. And syntactic sugar is sort of like a macro, which takes high level abstraction. This sugars them down. And this is what we are going to build. Maybe we won't cover all of them in the lecture, but you will see how knowing how to do if and while, how to do the high level stuff. So we'll drop units. We'll stick just with plain arithmetic over integers. But we'll introduce something else into the language. We'll add functions. So let's go through the constructs one by one so that we know what it's doing. So the program is now a sequence of statements. This is a statement separated by semicolon from another statement. A statement can be just an expression. This is the last expression of the program. You can think of it that it prints the value just like in the Google calculator. And here is a definition of a function. We have a function name, list of parameters, some number, 0 or more. And here is a body. And in the body, we have a statement which, again, can be anything, including another function definition. So for expressions, we have numbers, identifiers, which are variable, arithmetic expressions like before in parentheses. And then we have this construct here. What construct is that? It could be. Definitely, we could decide that this syntax stands for tuple or a list. But given the other constructs we have in the language, what do you think this construct does? It would be a call of a function, right? If we have a way to define a function, it probably makes sense to have a way of calling it as well. And this is a function call. So this expression is an expression that evaluates to what? To a function, function value. We can directly use the name of the function or we could have another function returning a function. This thing is a pointer to a function, if you will. And then you call it with some arguments which are arbitrary expressions evaluated here. Because these are formal parameters of a function. So I can define a function factorial. And then I would have x here. And then in the body, I would define the body of the factorial. And so when you define it, you always typically have just names of these parameters. When you call the function, you can call factorial with something like 1 plus 7. So when you make a function call, the actual arguments of the function call can be arbitrary expressions which are evaluated. And then you call the function. But in the definition, this is the parameter which can then appear in the body of the function. Now what the star represents? The star represents that this can repeat zero or more times. Let me write it as a zero or more instances. It's a convenience of a regular expression to show that a function definition can have zero parameters or more. OK. So why is this E? Good question. Why do we allow in the function call an E? We could perhaps do just ID. You can evaluate an expression that returns a function. That's the right answer. And so you could write a, oh, damn it. And so this could expand into, say, foo, which you call, returns a function, which you call. And that may return a function, too, which you then call, and so on. In fact, we'll write such code today. Functions, as you remember from schema, just value. So why couldn't the function return a function? In fact, this is such a common pattern that we'll use it a lot today. So we have a language with functions. Sounds great. We don't have local variables. Let's add local variables. It seems simple enough. Let's see how we'll do. So this is the part of the talk where we talk about names, a bound to slots or name. Like we had units bound to values in the calculator. Here we are going to name bound names or variables to slots, locations where we keep their values. And scopes will be implemented with frames. So let's talk about that. So the first design issue with local variables is, well, how do you introduce it? So let me show you two patterns. So here is what you would have in ALGOL or JavaScript. You saw this. You want the local variable in the function. Introduce it just so. So this A here is, you could say, a binding instance. This A says, I'm introducing A and now a new variable is created. Here is something else. Here is how Python would do it. Python would say, the shorter the program, the better. There is some good argument behind that. And what Python does, it says, oh, it looks at the body of the function and if it finds an assignment to a variable here, and so here is one, here is an assignment to A. So as a result, it will sort of under the covers input here var A. This is not legal Python. This is under the covers. You can think of it as this is what the Python interpreter does. Do we have a preference for one or the other? Are they equivalent? We just use a different keyword to decide when a variable is introduced. In one of them, we use var as the keyword for introduction. In the other one, we use assignment for introduction. You have a preference for one or the other. So who thinks we should go with the first? Who thinks we should go with the second? Who thinks it doesn't matter? OK, so let's not discuss it here because there will be an example where we'll see what happens if we go with the second and we'll go with the second. So we'll go with the Python one, which means when we see an assignment in a function, we say, oh, a new variable. We'll introduce it, OK? So let's see what happens. When a function is involved, we create a new frame for the function. This is where the local variables, parameters are stored. You push it into call stack. You scan the body of the function if it contains an assignment. You add the slot to that frame and you bind X to that slot. You could say there is now a new slot. Here is where we keep the value of X, maybe it's 17. And in this slot, we say, well, this X is bound to this slot, right? So this is our slot and here is the name. This is roughly what happens when you read a variable. What happens when you read a variable? You run the program, you encounter X on the right-hand side. What will you do to find its value? So you want to evaluate an expression, you are an interpreter, you find X in the expression, how will you find it? You will implement it in the first assignment, so in case that's a motivation. OK, please, in the back. Yes, you. So you look into the frame of the current function. Well, what if it's not, that's correct, but what if it's not in that frame? Then what? Frame's parent, all right? So let's assume that there is only one parent for now, right? We have the frame of the function and there is the global scope where all the global variables reside in their own frame, that's where we look. Let's go with that, OK? Now let's introduce one change. You look at the code, you don't like it, obviously. What would you change in the language so that the code is more readable, easier to write to? Yeah, a little proposal, OK? Somebody who didn't speak yet today. What's ugly about this? I guess it's your turn. Well, I think the helper has a lot of parameters and kind of practice all the parts. Well, wouldn't there, like, when you're calling the function? Uh-huh, so it is indeed the ugly part and the helper has too many arguments, right? Now that's the problem, what's the fix? What's a fix? You could have helper be able to access the variables from main. Uh-huh, OK. Make a dynamic. So we'll nest helper inside main so that it can access main's variables and we don't need to pass them, it will just, right? Access them, all right? So the solution is to indeed nest helper, like you see here, and it still has some arguments. These are the things that change a lot. The things that don't change a lot, like date and time, it will just access from its lexical scope, the scope of its parent, OK? See any problem with that? You cannot call helper from anywhere else, that's true. But perhaps we don't intend to. Maybe this is a helper for main. And in fact, it's nice to hide it in main because nobody else should see it for modularity reasons, right? So we are happy. We reduce the number of arguments helper is not seen. Sounds good? You see a problem. So right, so the local variables date and time in main, right, could change between the calls to helper. So indeed you could make a change here to date and time. But that's fine. That's exactly what we want, right? If they change, we want helper to get the current values. So this is fine. So given what we design in our language, do you see a problem? So we can't access the ones in the middle, main. Okay, so we have two scopes, right? We had the frame of helper and then we said we have a global scope. So that's excellent because you should have designed Python because if you look at this Python, it was a buggy program in version 2.1 and below. What do you think this program did? Clearly they didn't talk to you, so they made a mistake that survived a long time. Can you venture and say what the error would be? Yeah, it cannot find the factorial. So you call enclosing function, right? And that wants to call print factorial and the error is essentially factorial is undefined. So let's see to understand what the error is. What are the scopes that we have? How many scopes do we have? Two, three, four. Who thinks we have two scopes? Three, four, okay, three it is. So where is the first one? It's this one here, right? How about N? In which scope is N? The same? So is this scope like this? Yes, okay, good. So the next scope will presumably be like this, okay? And then there is the global scope which contains the enclosing function, right? So we have global, say, non-local and local, okay? And indeed, the way we did it, we have frame for a function and then we only look up the global frame we would miss factorial because it is defined in which scope. It's defined in this middle scope, right? So factorial is defined here. So you should know by now from 61A and maybe B that the outer scope contains enclosing function, the middle one defines factorial, the inside one defines N. These are essentially the names that are in those scopes. So the problem is to describe here is that essentially the notion of scopes did not really match the implementation, right? Every scope should receive its frame but it didn't. So you had more scopes, you had nested functions and therefore you had more scopes. Not all of them received its frame or you didn't look it up and this led to this error. So essentially we did the exercise before how many scopes there are. So this is the first train wreck. So we talked about scoping rules, nested functions and how these frames implement the scopes and how we need to look them up. These features need to be considered in concert. So now we have nested scopes which means that we have frames that have parents. So we could have multiple definitions of the same name. We can have x defined locally and we could have x defined in the parent frame. So and we could have recursion. We could have a bunch of these names. So if I look at the call stack and here is one frame and here is another frame and another frame, I could have x here and x here and x here. And maybe I have y here and z here. So if I'm running in this function and I need the value of y, what do I do? I first look up my own frame. If it's not there, then I go to the parent, right? So I go to the parent and then I go to the parent and so this is our call stack and the call stack defines the parents of frames, right? You happy with that? So the call stack grows as we call functions. We call a function. We push a frame on the stack. When we return, we pop and the parent would be up in the stack. OK, so let's go. Let's assume that's the semantics we have. We can spell it out if you want. You should really write down what happens at call and return. You push frame, pop frame. When the name is bound, what do you do when the name is referenced, right? What do you do with those frames? Now let's go to control structures and see how our scoping works with those. So what we want to implement, we have just function calls, but functions are first class values. We can pass them around. A function can return a function, then we'll call it. On top of it, we'll implement these control structures like this. There are many others like exceptions, coroutines, and continuations. We'll look at some of those later, but today we'll just do ifs and whiles. So assume that I'll give you, I need to give you a little bit of an extension for the language, and I'll give you a built-in conditional. I call it ITE. You can think of it as a built-in function call. OK. And what it does is if v1 is true, then it evaluates to v2. Otherwise, it evaluates to v3. So a simple switch like the C question mark operator, right? That's a built-in function that we'll need. I don't see how we can get around without it, but you can tell me. So it looks like this is really all we need. In fact, this is our if statement. And so we can use it to implement, say, factorial. So if you listen now and think about it, this will help you quite a bit with your project. So is this a good factorial, bad factorial, sometimes good? Depends. See, OK. So who likes the factorial? Who doesn't like the factorial? And other people don't care about the factorial. That's a fine position, too, but soon you'll have to care about the factorial. Because we'll test your programs on the factorial, too. OK. So what's the problem with this factorial? What's the guess? What will the program do when I call it with factorial 7? Like it's only a body weight, either the second or third argument depending on whether the first one is true or false. OK. Way ahead of me. So this is what we want. But what will the program do if we call it with factorial 7? Will it crash? Will it do run? The program will run forever, right? So this one will never terminate. Well, once it runs out of memory, I suppose it will. And you know why it never terminates? So this has to do with our, how do we pass the value? Are we passing arguments by value, which means when we come to the right hand side of a function call? This is a function call as far as the language is concerned. The function is in a library, but it's still a function call. The function call does what? It finds an argument. It's an expression. It evaluates it into a value, and then it passes the value, right? It's a so-called call by value. There are also things like call by name, which essentially lays you, pass the whole expression. That's evaluated later when needed. But we're assuming call by value. Like in schemes, C, and all the languages, you probably weren't. OK. So it wants to evaluate this. What has happened during evaluation? It calls factorial. It calls itself, right? And it keeps evaluating. Would it ever terminate? Well, at some point, the argument becomes negative to factorial. So you would assume that it terminates, but it won't. It just keeps evaluating the right hand side. It keeps evaluating this and never terminate. So how do we get around this? Cannot apparently even write the factorial. So what do we do? Please. We'll do lazy evaluation, but I don't want to introduce the lazy evaluation into the language. We did it with the Google calculator, but it really changed the entire interpreter. I would rather not do it. I would like to stick with call by value, which means eager evaluation, and do something else. We could make the ITE, which is if then else, a special form which behaves differently. We could make just that lazy, right? And only that would evaluate the left hand side or the right hand side. But I don't even want to do that. I don't want to have special forms. I want to have clean interpreter, and you want to have it too, I think, because it's simpler. So if not that, so not laziness, not even one lazy construct like in least for scheme you probably saw conned, which was essentially different. Because it evaluated either one or the other, but not both. What other options do we have? OK, that's good. So how do we do callbacks? What does a callback do? Yeah, please. We could somehow have an expression that uses a lambda. So we need to wrap things in a lambda. How will that work? So why would lambda make do the trick for us? So the beauty of lambda is that you can evaluate them later, essentially. The code here, n times factorial, we put into the body of the lambda. That doesn't run until the lambda is called, right? That's the trick. So how do we do it? So we define now this if-else construct. The thing of it is this is our if statement, right? In terms of IT, can you suggest how we'll implement it? So let's do a little bit of the peer interaction here. And this is something that you need to get. The rest will be easy. So here is a skeleton for if than else. It's a function we want to implement, and somehow it needs to behave like an if. So we somehow want to use lambdas to delay the evaluation so that it doesn't evaluate eagerly and never terminates. So, too? Yes? I just ask a question. Is ITE, the new version, or is it still the old team? You know, the new value version? ITE is still the eager one. ITE is still like before. It gets the arguments, evaluates both. So it's not like the column from Steve or from Lisp. But you can use lambdas to implement the true if than else, which only evaluates things when needed. Three arguments to announce. Well, that's part of the design question here, right? The question is, what are the arguments? And the answer is, that's part of the question. So lambdas are in the language. They look pretty much like in JavaScript, right? So you can write function or lambda, whatever, with the body. And now I think what we want, we want to define this if else in such a way that we could then use it to implement a factorial. So maybe look at it this way. How do we implement factorial considering that the only thing we have is this ITE, which evaluates everything? So we need to wrap something in a lambda. So talk to your neighbor and try to scribble it down on how you would do it. I can, clarification is OK. Can I clarify something? I'm going to make it a second. And then I'm going to have to write it down. We have to do a little bit of a section to get a little bit of a volume challenge. We'll stop here. So therefore, you have to know how to do it. So make sure that you call ITE. And this is the first thing that we're going to do. You're going to get a little bit of a section where it's going to follow you. And then you're going to call it. So you just call ITE. So maybe let's start by writing factorial, like you see here, which would call actually if-else. So let's write the factorial that would be implemented on top of this if-else. So essentially, we are programming above the abstraction that we want to implement. Maybe that's helpful. Maybe we'll figure out how we'll call the if-else abstraction, which is our if statement, and then we'll figure out how if-else is implemented. So how would you write the factorial? So I can tell you there is only one call if-else, which is the one if statement. Here is an expression, then there will be the true branch, and then there will be the false branch and the parentheses. So what goes here? How do we write the conditional for if-else? Yeah, let's try. Just the same thing should be n less than 2. Now, I can't erase it. Let's put here the else branch, not the then branch, but the false branch. What will go there? So there will be a lambda. No arguments. And what's in the body? The same thing as before. The same thing as before, which is n times factorial n minus 1. Now, what do we put here into the true branch? Yes. OK, so the answer is lambda again with no arguments and the base case 1. Why cannot we just say 1? Maybe let's look at first at the definition of if-else. And we'll see then why we cannot pass into if-else, the 1 directly, why it needs to be wrapped in lambda. So what happens in if-else? We clearly use if-then-else. We'll call this c for condition then and else, maybe true and false. So what do we do with if-then-else? c, t, and f. OK. And so let me just assign it into some function. And then I'll call this function here just for clarity. So if-then-else returns a function, either true or false, depending on the conditional. And then it calls it. I can rewrite it by the way by calling directly the result of the ITE, but this is perhaps clear. So where is this evaluated? We're evaluating this right here. Is that always a good idea? So let's look at while. So this is the factorial. You can think of it this way, right? You can define the functions explicitly and then call them like this. This is essentially the code for if-else. So just delay the evaluation and then you evaluate it actually here based on the value of E. These functions can be anonymous. They don't need to be named like they are here. You could say exactly that. You saw that in JavaScript. If you want to do if, which is if without a then, you just put an empty lambda here so that when this call happens, it actually invokes a legal lambda, an empty one. OK, no magic here. So a little bit about this. So what do you saw here? The way we wrote this factorial with the explicit lambdas is ugly, right? You don't want to write it as a programmer, even though in jQuery, this is normal construct. This is how you register callbacks. Here is how you delay evaluation. It's not so bad, but if statement actually looked pretty ugly. So what we'll do, we'll do so-called de-sugaring. We'll allow you to write in some clean, high-level syntax, which looks like plain if and plain while. And then after the parser, you walk over the tree and you translate it into this lower level form. You essentially insert the lambdas. And here is a simple example of de-sugaring, right? So look at this. Our language does contain already this assignment. And it does contain this function definition. It has these two separate constructs. But now, it would be nice to think whether we do actually need both of them. Do we need a construct for function definition in the interpreter? And do we also need a separate assignment? Turns out that, no, you can take this and rewrite it between the parser and the interpreter into this form. And if you look at this closely, you see that this form is nothing but a special case of that, of the assignment. The right-hand side is anonymous value, and you are now introducing a variable that has the same name as the function. So if the de-sugaring rewrites this into that, your interpreter only needs to support this form, this construct. It doesn't need to support that form. Only the simple de-sugaring rewrite needs to understand that. So you can test it in, say, JavaScript and see that this, in fact, works like this. But you can assign fact to a variable and call it. So why am I calling this? Because a lot of your assignment will be about taking these high-level forms and rewriting the tree into lower-level forms without touching the interpreter and effectively keeping the interpreter simple and developing new constructs. So here is vial. And vial, you could say, is just like if, except there is something fundamentally different here. So can you spot the change? So first of all, let me make it clear, in case it isn't, that this vial here is just another library function which we introduced, like before we had if-else. So vial is a new library function, and the de-sugaring will take a normal-looking vial like in JavaScript or Python and rewrite it into a call to this vial. But we are calling this vial differently than we call this if. What's different here? Are we delaying anything less? Are we delaying anything more? So now we delay the evaluation of the conditional as well. Presumably, we want to write vial c is greater than 0. Sorry, count is greater than 0. But we are not evaluating it directly when calling that function before we did, but now we don't. We are passing a lambda. Why is that? What would happen if we evaluated it directly right here when calling vial? What would go different than we want? So the expression, count greater than 0, would be evaluated how many times? Once before you call the vial. What is vial supposed to do? Check it each time in e-iteration. So we need to pass it a lambda that will be evaluated inside vial in e-iteration. So we need to delay that. Essentially, we need to provide that expression. And so here is vial. So you see that vial now evaluates it here, calls this lambda. Now this is x is our condition. And it executes the body here once. And if x is true, it also calls vial recursively. But again, it passes the lambda, not the value. So that the next recursive invocation can take care of it. Please. I'm not sure I can just ask again. Oh, the question is, could you take the vial and wrap it itself in a scope so that you can have local variables to vial? That might be useful depending what constructs you want. And yes, so in fact, we'll get to it maybe today, maybe later, that this actually matters quite a bit. OK? The reason without the count on the right side, wouldn't that just create a new variable? Like if you want to just have count equal to 0, without model, wouldn't that create a new local variable? So you're asking here, right? Yes. So you figured out if you didn't want to create a new variable? OK, so you are, in fact, let's see if I am ready to talk about this. Yeah, so we got to it now. Let's see if I can. All right, so this code indeed is broken. I wanted to cover it later, but let's talk about it now. So what is broken? Actually, let me get to it. We'll do it a little bit out of order. It's not so far away, actually. So we have the vial loop. Maybe I change the slides in an unlucky way. So here is the code, and it doesn't work. Let's see. I think this is what you are suggesting. What if the code actually looks like this? No, you were actually referring to a different problem, but let's look at this problem. We made some unfortunate decisions that it's really important to understand. So here you see the vial. It should be straightforward. It's like if, but the code looks like this. So what will go wrong? So how many people can spot what will go wrong? Can you see the problem? OK. Yes, please. So the count is, oh, I see. OK, so I meant to change this to x, actually, as well. Sorry, that was too easy. Yeah, so now there is no count. There is x here. So now this is a problem. And if you see it, it's pretty good. Lisp had this mistake in its semantics for quite a few years. Only when people started writing bigger programs, did they realize, oh, we didn't define something right. Then Scheme fixed it. So you see the problem here. You do? Our scope here is not this good. It's dynamic. And so the x equals e will override our accident in the collision. OK. So the problem is, but let me clarify what we are talking about. So let's assume that how does the code look like? We have the main program. And the main program does what? It contains in its frame an x. Then we call vial. The vial creates its own x. So this is this x. This is that x. And then vial is going to call this lambda. This one here. And this also has an x. So we have several x's. How many x's do we wish to have? We want to have two x's. We would like to have one x in the vial. So this x is supposed to be one x for the vial. And the other x should be this one here, which should be the red one. But what scoping do we have? So we decided that we'll have dynamic scoping, which is when we want to find the value of x, we go to the parent frame, which is the next frame on the call stack. That's called dynamic scoping. It goes through the frames as they lie on the call stack. And so when this lambda, or that one for that matter, is called, where will it find its x? To keep it simple, let's assume that this is this lambda. Now, when we invoke the lambda that is conditional, we need to find this x somewhere, right? Where will it find it? We want to find this x in the frame of the main, right? We would like this x to refer to this x, which is stored here. This is the x from main. This is the x from vial. This is the slot for this guy, OK? When we call this lambda here, is it going to find the right x? It's going to find the one that belongs to vial rather than the one that belongs to main. Because we decided to use dynamic scoping, which walks up the stack. So if somebody calls a function that intervenes on the stack and uses the same variable x, we are going to look up an incorrect variable. So what is the right solution to fixing the problem with dynamic scoping? Dynamic scoping is clearly bad, because if you ask somebody to write the vial function sent in China and then people in your Indian branch write the main loop and they use the same variable x, you will get these naming conflicts and all of a sudden code will not work. It's not modular. The two functions don't work together because the x is intervening. So static scoping or lexical scoping is the solution. How does that work? Exactly. So essentially, what it's saying is that a function is no longer a pointer to the code that you run. A function is actually a little bit more. A function is a pair. The first one is that code that you run, the lambda. And the other one is that pair and frame. So the function now carries with itself the frame within each it was defined. You remember double bubble from 61a? And so the other part of the bubble is that parent pointer. And so you are not looking up things on the stack, but you are following the parent link that the functions actually introduced. And this parent link would bypass the middle x, because the function lambda here, this one, was defined such that the parent of the double bubble points where? This lambda now is a double bubble and it points to this frame bypassing the while index. So this is another train wreck that happened a long time ago, because people didn't realize that you may write code in this particular way and that these naming conflicts could happen. So I want to talk a little bit for a minute about syntactic desugaring. Let's see where did we end. So we talked about things that syntactic desugaring allows you to write code that looks like this. You can write if with n greater than 0, parenthesis, statement, and then you can do else and a statement. No lambdas are visible for the programmer. Our parser will parse this nice syntax for you and give you a syntax tree like an x expression. But the interpreter will not understand while, for, and if. So you need to turn it into something that has only lambdas and lambda calls with it. And that process of transforming the tree that you get from the parser to the tree that your interpreter will understand is called desugaring. So your code may look like this. You have while. Looks like beautiful JavaScript while. The parser will give you an AST which does contain a while node because the parser doesn't know what to do with it. And then you need to walk over the tree and turn it into an AST without a while node. How do you get rid of the while node? You rewrite it into a call into that while function. You take the body here and the expression. And what do you do with it during the process of the rewrite? Exactly what we did by hand. You wrap it into a lambda. Essentially, the parser gives you a tree that will look like this while. And now it has an expression here and a statement. And you will turn it into a tree that will look like this call. What will you call? You will call this special while function that we wrote by hand. And we'll pass it two arguments, right? The first one is lambda whose body is what? The E. The right-hand side is lambda whose body is what? It's the statement. So in the process of the rewrite, you find in the tree each instance of this. And you rewrite it into each instance of that. And now the right-hand side does not have a while node. It only has calls and lambdas. And the interpreter can now implement the while. The same for if and so on. So just one last question to make sure you want. I'll clarify this before posting it. How will the rewrite happen? Are you going to rewrite it bottom up or top down? Turns out it doesn't matter. You can do it either way. But it's probably easier bottom up because you know that the bottom of the tree is already rewritten when you are handling an old. How many times do you need to do the rewrite? Is it enough to go through the tree once? Well, you should go over the tree until no rewrites remain. Maybe in the first pass, you change the for loop into while loop and then in the second pass, while loop into an if statement and then if into the call to I.T.E. Until there is nothing to the sugar. You keep going through the tree again and again. Well, you need to carefully design the rule so that they so-called normalize and eventually they turn into a program with just calls and lambdas. I'll fix the order of the slides before posting it. But have a look at the handout at the starker code and you'll get help with the project intersections tomorrow.