 Thanks, good morning everyone, thanks to be back here with you in awesomely awesome phoenix. Awesomely awesome phoenix. That's good, as we say. Anybody, so what we're going to do today is we're going to continue with where we left off. Kind of give me an idea of what are the big differences between static scoping and dynamic scoping. Symbol table changes, also changes when? Changes, okay, yes. Static scoping resolution of a name is done at compilation time. Dynamic scoping is done at one time. Great, so yeah, so the big difference is, if I give you some code here, say here. If I said, okay, what does this reference to variable C, what does this refer to? What definition, right, doing that resolution? What is this name C at this instance here? What definition does that refer to? So can you tell from this C here? The char C. The char C, right? And you can tell statically, with static scoping, you can tell just by looking at it. The same thing with this variable X here. Right, so what is statically looking at this, what does this X refer to? The internal index. The index, yes, exactly. But at runtime with dynamic scoping, right, we have no guarantees that that actually refers to that X. Right, we can know this statically because we statically create the symbol table as we're going through here. So when we look at this reference X, we look at our symbol table, we say, is X in the current scope? No, X is not in the current scope. Is it in its parent scope? Yes, it's in the global scope X. So I know at compile time that this X refers to this static X. Does that make sense? Okay, so dynamic scoping, right, is this building of the symbol table. So in static scoping, the compiler itself is actually building the symbol table while it's going through in order to resolve names to declarations. Right, so it's doing this resolution at compile time. Whereas with dynamic scoping, as the program is being executed, names are being resolved to declarations. So this is happening while the program is executing. So this symbol table needs to be created and updated at runtime. So the rules for how you look this up are identical. So that's the important thing to keep in mind. The rules of, hey, I need to resolve X. So what do I do? When I'm resolving a name, what scope do I look in first? Current scope, and then what after that? If it's not in that current scope, then where do you look? Does the outer scope global? Not necessarily global, we don't know. The scope above it, right, the parent scope. And then if it's not in there, where do we look? The parent scope. And then we keep doing that. Then we're at the top, we still can't find it. Then what do we say? Error. At the very top, the very top scope is global, right? So we'd say there's a compilation error. The fact is you use a variable that's never declared. So this is exactly what your compilers are doing. The only difference is with dynamic scoping, this is happening at runtime. What this means is that X can actually change depending on how, this means that some variable X can change depending on how a function is called. So here the scopes that we're looking at are actually the call stack scopes. One function calling another function. Those scopes, not the lexical scopes that we can just look at and say, okay, this scope is valid here and here. So this is actually not, we'll talk about it later about why this could be useful or not useful. Any ideas off the top of your head? So why, which one are you used to using? Static. Static scope? Plus you. So why would you care about dynamic scoping? It happens at runtime. It happens at runtime. It does not happen in every language at runtime. So languages get to decide which one they support, right? So this is how your C, the C code, in Java code you can look at and tell exactly what that variable is going to refer to at. You know that beforehand before it runs. So why would this be useful having these things change? It would be easier on your memory. Easier on memory in what sense? Well, I mean, because you don't have to keep referencing a very, like memory accessing a static symbol table every time. You're just able to update as you go. So it's kind of, you're referencing it from a more local perspective. I don't know the words to describe it. I could see that, that being a possibility. So at least it makes compilation simpler, right? Because when you're compiling you don't have to keep track of this symbol table, right? But then you kind of move that complexity into the runtime, or the runtime has to do that. Yeah. Can't with dynamic scope increase you have some extra functionality that it enables so you can, you know, have your variable change. I mean, I can't think of a good example to describe what I'm trying to say. Yeah, so you can kind of think of, these variables could kind of be parameters to your functions in some sense. But they could be functions that maybe you up top, you call a function, what's called a function, what's called another function, right? Which is using some variable. Well, you can't directly pass a variable into that function, a parameter in that function. But if you can define a new variable, for instance, Emacs actually uses dynamic scoping. And this allows you to change variables for the size of the font and the number of characters on a line only in this specific instance. So you can do that by setting these global variables. They kind of be like configuration options. Let's look at an example. We can kind of look at some things. Okay. So we have our program, right? We have the global int x. We have a global bar, a global function foo, a global function baz, global function bar, then we have our function main. So if I asked, using static scoping, what is this going to output? What's the first thing that's going to output? And a new line, right? Testing followed by a new line. So we know it's going to start executing a main. We know this x here statically is going to set this global x to be 10. And then inside this block, this x is testing. So it's going to output testing. And then it's going to call foo. So foo is going to get called. This local c is going to be set to c. Bar is going to be called. This is going to set x to be 100. Baz is going to be called. Then we're printing out x. So what does this x refer to statically in Baz? 100. Statically? How does it refer to this? Not statically. It'll grab the global, right? Yeah. It'll be the global, which was 10. Yeah, exactly. So let's do this statically first. So it should output 10. Then this x is going to reference which variable? The global variable. Set it to 1, 3, 3, 7. Then Baz is going to return. So we're going to be here. R is going to return. So we're going to be here. And now we output the value of x and c. So now it's the value of x. What does this x refer to? The global x, which is 1, 3, 3, 7, the value. C is what? Character c. So we output that. And then we return from foo. Which means we return here. And we're done. So statically, we can know exactly what that's going to refer to. So let's see how this changes when we do this dynamically at runtime. So we got to think, OK, at runtime we're basically going to go through and execute each of these lines. And as we do this, we're going to build up the symbol table. So our symbol table we're going to represent as having a name, some metadata, and some metadata. And we'll have it have the value there. So it's easier to think about. So when I see this declaration of x, what am I going to create in my symbol table? Yeah. So it has the name x. It has an int. And what does the c standard say an uninitialized variable has the value of? Doesn't. Doesn't. We don't know. It could actually be anything. If we try to read from that, it'll be undefined. So let me go to the next one. So is this going to create a declaration in our symbol table? So what's the name? Bar. Bar. And what kind of object is bar? What is bar? It's a function that returns void. And right now, we don't have a definition for bar. All we know is that as the programmers are declaring, hey, there is a function called bar, and it returns void and accepts no parameters. So we get the function foo. Bless you. What? So then what are we going to create in our symbol table? A function which returns void with some definition. Yeah. Exactly. So this one, right, the difference between foo and bar here right now is that we have the definition of the function foo, right? We know it exists online for. So it's just an abstract way of saying, like, hey, this is where this function is. So when we want to call it, we know where it is. Or you can think of this code in here, whatever. It doesn't really matter. The idea is when now, when we see a call to the function foo, we know exactly where to go. Okay. Function baz, same thing. Okay, function baz. We're going to do line, we're going to say that it is a function also that takes turns nothing and it's defined on line nine. Now I'm going to get to this. What am I going to do? Yeah. Go back to bar. Right. I'm going to update the entry here to update it and say, hey, look, the bar is defined on line 13. Right. Here's where the definition of the body of bar is. So you're extending. Extending it. Extending the definition of it. In some sense. Yeah. You could say that. But the important thing is you're not creating a new entry called bar in this single table. Right. We look it up and we say, oh, bar, we've already declared bar. Great. Now let's, now we have the definition there. So if you think, so. Okay. Then we get here into main. So then what are we going to do? Yeah. We have a function called main that returns an integer which I didn't put in there, but I should. Or at least it accepts nothing. So the parameter is void and it's defined on line 17. Right. So now we get here one of the semantics of C. What's the first function that gets executed? Main. Main. So now we know we can start executing it main. Right. So we execute main. So what do we need to do? We see this ID called x. Then what do we need to do? Make a new one. Or is it updating it? Yeah. We do need to update the x, but we first need to ask what is x. Right. So how do we resolve it to some declaration? Right. So how do we resolve this x? Well, we know it's in the symbol table. Yeah. So we're looking at symbol table. Right. We would first look in main scope. Right. Main scope. Main doesn't declare any variables. So I didn't make a scope here for main. What if you gave it a double? X equals some double. We'll see that in a different, in another example. That's a little language. No, it should be fine. We'll see that in a second. So we, so which x does this refer to? The global x, right? This entry here. So we're going to update the value here of x. And we're going to set it to be 10. Right. That's what the semantics of that instruction means. Now when we enter this, when we enter this new block, what are we doing with our symbol table? Are we in the global scope? No, no. We're under main. No, we're under main actually. So there would be another scope, but there's nothing to find there. Right. But here we've entered another scope. And so I'm going to draw this kind of with arrows to show how this, like to separate the symbol table by scopes. Right. So to say that this, so inside, right, this scope, this declaration says, hey, in this scope here, I have a character pointer called x that has the value of testing. And I know that I'm not updating it here because this is a declaration, right? This is saying, hey, we definitely have, I'm declaring that there's a new variable called x. And the type here is a character pointer. And the value is the string testing. So when I get to this line, I try to resolve x. What do I do? Print the most local x. I first need to resolve, right? So I look up x and I say, what does this x refer to? So I first look up in kind of bottom up. Right. Yeah. So in the closest scope versus to the widest scope. Right. So the closest scope is at the bottom here. It's this scope inside this block. We say, OK, I see x. Great. It's a character pointer. And it's the string testing. So it's going to out. So it's going to say, OK, resolve this x to that value here. And so it's going to output the string testing. So then what happens when I go to the next line? We're leaving that scope. So we no longer care about that symbol table. Exactly. That's pointing you somewhere else. This, because of the scoping rules, right? C is block level scoping rules. So as soon as we exit this scope, we know we can totally clean up and get rid of that, that blocks scope. Right. So we're completely gone. We don't care about it, because we know that after that scope, we can't ever refer to this character pointer x. Right. Because that definition of this character pointer x is only valid inside this scope, inside this block. So once we leave, we clean that up and we get rid of that symbol table. Questions on that? That's an important point. I don't know, to me, it doesn't seem like a big deal, but OK. If you don't do it, what would happen? We get all confused. Yeah. Don't do it. If you're going to have this x hanging around, your next declaration of x is going to refer to that x that's defined in this block. But the scope of that block is only these two lines. So there's no way you can refer to that x outside the scope. So it goes away. OK. Then we call foo. So when we get, so we're going to call foo, right? And how do we know which function to call? Because we define the search of both. Yeah, we first look it up in our symbol table, right? Because it was defined above. We search above and we say, OK, foo is a function. It's void. It accepts no parameters. It's on line four. So we start executing in here. Now when we get in here, what have we entered? A new scope. A new scope, right? So we're going to create another scope in our symbol table. And what's going to be inside that scope? What's declared in here? A character named C defined with the character C. Yeah, a character named C that's defined as initially having the character C. Then we call bar. Then what do we do? We first look up bar. Yeah, we first have to look up bar, right? We have to resolve bar because bar is just the name, right? And we all know, I mean, we've maybe done this before. If we try to call x as a function here, we would expect an error, right? Because we'd look up x and we'd say x is an integer, x is not a function. So this is an error, right? Or if we put gobbledygook in here, we try to look that up in the symbol table, right? It's not going to find anything. So it's going to be error. You have an undefined reference to some function. So we see that bar is on line 13. We jump in here. Now that we've entered bar, what are we going to do? Enter a new scope. Enter a new scope. So we're going to create a new entry. What's going to be inside the initial bar of that scope? Oh, that's going to make us update. Oh, wait, no, that's a... We create a new int x to find out. Int x with value 100, right? This decoration here. When we go in Baz, we look at Baz. We say it's Baz in our local scope. Nope. Is it in our parent scope? Nope. Is it in their parent scope? Yes. Yes. So it's a function void on line nine. Great. I'm going to start executing from there. New scope. I do that. I would create a new scope, but it would be empty because there's no declarations here. There's no declarations in Baz, right? It's an update. So now when I try to resolve x, what do I do? Use the global. Use global. Use the global scope. The parent scope, then parent to parent. Yes. Check in my scope. My scope has nothing. So I check in the parent scope. I say, is there a variable named x in my parent scope? Yes. There's a variable x. I'm going to print out what now? 100. 100. So this x, because of the symbol table, is going to reference the declaration of x that's actually in bar. And this is because that we're resolving these symbols dynamically at runtime. So you can maybe try to see if we didn't call bar before calling Baz, then Baz, this x would refer to the variable x. It's the fact that bar declared a variable x, a local variable, that Baz now when it references x, it looks up the symbol table and it says, OK, this is the x. Oh, its value is 100. Great. What do we print out in the static case? 10. You're printing out 100 questions on y. So for static, if there is no local variable x, it defaults to the global x. Not quite. Well, it looks out in scope, what they call lexically or statically. So here we'd say, OK, like this x looks in its local scope. If there's nothing in there, it would look in main scope. Because if there is a declaration of x in main, then the x in this nested scope would still refer to it. But if you had some screwy function that called main, that had an x in there. It doesn't matter at all. It would never check there. Exactly. Yes. Because everything is resolved statically. So it doesn't matter who calls you or what their scope is when they call you. All that matters is statically looking at this. So if it's not in the end, if it's not in the block, it's embedded in. Yes. Exactly. In the nested blocks, right, that it's embedded in. This is why they call it lexical scope, because it's the lexical at the kind of lexical level where you can just look at the blocks, how they are arranged. Where dynamic is happening at runtime. So depending on how, so this x right here, right, depending on how main is called, what other things are calling name, it could refer to some other x rather than the global x. So definitely not here. We're definitely not referring to this global x because that x is up here, and we already see that there's an x defined in our simple table. So when we go here, what is this x going to resolve to? Which x is this going to set the value of? The local one. The one that's currently set to 100, right? It's actually not even our local one. It's actually bar's local variable. The last known. Exactly. So it's going to update that to 1-3-3-7. We're going to exit baz. Baz does not have any variables declared, so we don't need to clean up the scope. We are now in bar. We get to the end here, so then what's going to happen before we return? We get rid of the simple table. Yeah, we get rid of completely the simple table. It goes away. And so now we're here in foo. So now when we print out, so what's this x, what's this first this c going to resolve to? C. C, right? It's going to look up in here in its local scope in the first scope in the simple table. So yeah, this c is going to output the character c. What's this x going to refer to? Ten. Ten. Is this ten in here? It's out in global. So it goes out one. Goes out one. But we look in this, right? We look in this simple table. We say is x in here? Then we go to the parent. We say is x in here? Yeah. Yeah, and then we grab that value. This is actually going to output testing 110 c. So first testing, then this one's going to output 100. And then this one's going to output 10 c. And then when we get past main, we're going to clean up. When we get to the end of main, we're going to, oh, I guess this isn't from me. Now with this one we get to foo. We clean that up. And eventually we continue and everything goes away. So if we were to compile this, is this how c is going to work? Can I make c execute this dynamically with dynamic scope in? By just saying dynamic gcc. Yeah. I don't think so. No. Yes, exactly. This is me. I have to do this. This is a fake program. This program does not exist. But showing, okay, if we were to be able to execute some kind of version of gcc that used dynamic scope in. I'm sure it's out there. Think about that. Isn't that more of a stack of a language rather than an island? Yes. I mean it would have to be like a dynamic version of c. So yeah, it would be the, this is the signix for the language. So if we output this, right? We saw it's going to output testing. And then what's going to output? 100. 100. 10 c. 10 c. No comma. Tricky. No. Print s. So questions on static dynamic scoping. This is how I recommend you do this when you're doing an exam, right? To actually build this simple table as you're tracing through the program. Yeah. So on the computer level, this simple table is represented via the stack, right? Say it again? So in actually during runtime, the way the computer keeps track of the simple table is via the stack. Right? Not necessarily. So it doesn't actually create this data structure. Yeah. It can create exactly. Yeah. It can create it on the heap or whatever. But it just needs this data structure. And it has to have instructions for every time it enters a scope, it creates a new scope on the simple table. And then when it leaves that scope, it pops it off the simple table. So that would be a functionality? Yeah. Well, the compiler would have to ensure that those semantics exist. So it would put in those instructions. But I imagine, depending on what compiler you use, the implementation of this can be different. Yes. Absolutely. Yeah. Why didn't you print testing 110c? Because main did that. Why didn't it print testing? It didn't print. Yeah. It was testing 110c. I mean, it didn't really output it. This is me doing this, right? I didn't actually run this. It does not work. So why would you want to do this? What might it be useful? It seems like we have to do a lot of work, right? We have to add stuff. And the compiler has to add in instructions. So actually, one of the reasons is really kind of related to how we were doing this here. So we're doing this here by going through line by line, right? Stepping through, executing each part of the program, right? I think we talked about the difference between some compiler and an interpreter, right? So this would be interpreting a program line by line, right? Going through. So it's actually, to do this thing statically, right? You have to go through and look everything. You have to resolve all the references, basically, either first or you have to keep track of a separate table of the static scoping rules versus actually the runtime rules. So it turns out that this is building this table. Building the table is a lot easier if you're interpreting it line by line because that's exactly how you look up and call things and you can just do that with here. So if you use dynamic scoping, your implementation of the interpreter becomes a lot easier. Yeah? What about duplicate variables in a function? What do you mean duplicate variables in a function? Can I declare index twice in the same function? Oh, like index twice here in May? It depends on the language, but usually I would say they would throw an error. So you can only have one name in one scope, right? But the important thing is you can have an index here and then inside when you have a new block, a new scope, you can declare another x. And inside that block you can declare another x. You can keep going as long as they're not in the same scope. And so I think the tricky thing would then come into play of what about parameters? What's the scope of parameters versus local variables? I think that's language dependent. Do you mean parameters on functions of the same name? Yes. Like if you had a parameter here called index you could declare a variable, a local variable inside that function scope as index. I believe in C you wouldn't be able to do that because they have essentially the same scope. So it should complain, but that would be interesting to look at. So would this mean then that creating a program with dynamic scoping would actually run is a lot more depending on the programmer being smart about... It seems like it would be hard for the compiler to catch a lot of these errors and would just be depending on the programmer. Absolutely. Yeah, so you can think about we've kind of pointed to it here where we have defined a character pointer, right, x with the same name as an integer x. So if I called inside this scope, if I called baz, now this x in baz is going to refer to this x in the scope here and now I'm going to be... And d means interpret that as an integer. So I think it would actually still work in C because these type systems are terrible. But in the normal language it would say, hey look, a character pointer is not an integer so this is a type error, right? So yeah, you have to be a lot more careful as a programmer that you don't define a variable name that something else down the line is going to use. It also makes... Basically, so here we know even dynamic or static, right? We can see that this x is always going to refer to this x because it's in its local scope, right? So that you don't need to worry about, but you need to worry about things like this when you have... We'll see... Maybe I'll call back to this when we go into the landing calculus. But this x isn't bound to anything in the local scope, in its scope. So yeah, when you use this you're kind of... It's in essence you're kind of saying this is some global x that anybody who calls me could potentially change. So it really kind of messes with the meaning of what a global variable is. But it can give you some flexibility in some sense. So like other programs that they know what variable to use, they can tweak them to their values of whatever they want them to be. So it's kind of this... You do get a little bit more expressivity and customizability, but at the price of... It becomes really hard to reason about these programs. This is why it's pretty much died out and most normal languages are static scoping. You know, there's all kinds of nightmares, right? Yeah, I mean there's a big kind of trade-off between how much power does the programming language give you to do cool things and how much rope does the programming language give you to hang yourself? So oftentimes... And actually what's better can depend on what you're doing. So it could be companies may want it to be harder for you to shoot yourself in the foot, right? So that way kind of everybody's at this one level and maybe it's hard to do really cool crazy stuff. Like if you look at like doing Ruby on Rails and doing all their magic methods and domain specific languages for defining models, like you look at how what that equivalent is like in Java, it's crazy, like crazy verbose, right? So you have this trade-off plus the Java code, you know, type checks and compiles, whereas the Ruby code is all this crazy magic that takes weeks to dig through what's going on if there's ever a problem. So yeah, all kinds of programming and language design is all full of these trade-offs. Yeah? I mean, is dynamic scoping just a better way to manage the memory dripply as the programmer? It really doesn't. It's a way to cut out ambiguity, isn't it? It kind of adds more ambiguity in some sense but you can't tell exactly what this variable who's going to define. But you're not using a bunch of memory as like on a heap or a stack because you're not statically... You're still, well you're still using this heap. Yeah. Or you're still essentially using this stack, it could be on a heap, it doesn't really matter. I think memory-wise it's pretty much the same at a high level, like I don't think there's any like huge magnitude difference here. Really, well, it is easier to implement so that's like the one big nice part about it but the downside is the programmer is hard to reason about which you'll see on some examples of midterms and such. Homeworks first. Okay, so when we were going through here and we saw this name, Baz, what did we need to do, right? So we needed to resolve not only this, the name X, right, to know where X was declared, but when we saw a function called like this call to Baz, what did we have to do? You had to go navigate and define Baz. Right, we have to resolve Baz too, right? We need to resolve functions. Are functions, I don't know, are they more complicated than variables? It's a weird question, I guess. Maybe you have hard to answer parameters. Yeah, right, so functions, they're actually, can be very similar, right? And depending on the language, if you use a language that has what they call first class functions, you can treat functions just like regular values. You can pass them into functions. You can have functions that return functions. You can build functions dynamically at runtime. But they do have a little bit more special meaning, right? They have not just the type, but they have the type of their parameters, the type that it returns. They have scope just like, you know, the name here just has scope just like any variable declared. So, when we're going through, we want to be able to ask, to answer the question, how do we resolve, just like we try to resolve ID usages to a specific definition of a variable, how do we resolve function calls to the appropriate function? So, what are some ways from your experience programming? How do you know when you see a function call, how do you know which function is being called? And don't say it because you right click on it and you go, say, go to definition. You search. You search? Maybe one way. Yeah, you can have the prototype, the declaration above, maybe the definition below. But what are you looking for? How do you know that this call, this invocation of a function calls which function definition in C? Let's do C first. The function signature matches. In C, what's a function signature? So, that actually also gets to what function resolution is. Isn't it like the type and parameters? Actually, no, it's just surprising. It surprised me too, in C. Memory address? Memory address? No, we're not. We don't care about memory addresses at this point. This is more higher level concern. Maybe when it was first declared? But how do you know? You see an invocation. We see Baz parentheses. How do we know which function is actually going to be called? If you look in the local and then in the parent. For what? What are we looking for? For that definition. Something that matches up Baz. What's definition? That same Baz. The name. The name, yeah. That's the name. Actually, in C, it's just the name. You can't declare multiple functions that have the same name but different numbers of arguments. The other way you may have been thinking about this before is in function signatures. It's the exact same idea. The idea is I see an invocation of a function. How do I know what definition? When I resolve that reference, I want to know what body is going to be executed. Which function is actually going to be encoded? The other way to think about it is, how do I know if two function definitions are different? It would be if they have two different function signatures. So what if could you use the names plus the return type? Could this be a way to resolve functions? Yeah? It's part of building a signature. But just the names and return types. Only names and return types. I'm saying, can you do it? So you can do it with names, right? Pretty easily. Could you do it with names and return types? You're designing a language, yeah. We have a bunch of different methods with the same return types, so we weren't really able to specify whether it's resolved. Say it again? You can have a bunch of different methods with the same return types, so it doesn't really specify what you're trying to resolve. The name is... Right, the name. So let's say, in this type of a language, so resolving functions is just on the name. So I have a call to a function foo. I look for a function declared in the scoping rules called foo. If I find it, I use that. If I don't find it, I don't use that. But could I use name and return type? Can I define two functions foo, one with a return type of int and one with a return type of a character pointer, and use that to resolve? It would work, but it'd be dangerous. I think it's a bad idea. It would very easily return something that we might not even know. So what about... Yeah, it's dangerous, right? Well, but why is it dangerous? Because it depends on the context in which you call it. Right, so it could depend on the context. Like int x equals foo or double x equals foo. Right, so int versus doubles. Yeah, you could have int x equals foo, double x equals foo. Or a char, an array of characters versus a string. But why is it... Here we're trying to resolve, right? So we're trying to resolve that name to that reference. So what is... It's dangerous because... But does the compiler know unambiguously exactly which one you mean? No reason if you want to... Okay, let's say that you have one function that... They have the same name. Okay, so you have two functions with the same name. One has a return type, and the other one has a return type. A char. Okay. So in your code, you have int x equals... It doesn't... The compiler can't figure out which one it wants. Which one is the right one that you want to call because you can represent a char as an integer or an integer as a char. So it can't really figure out... Or even what happens... What did you say the int? Or do you understand that? It's like a long or something? What happens if you're just casting it as a string? Because that's neither of those. Yeah, what if I'm doing... So can we resolve this foo to one of these two? You can pick one, but do you want your compiler to pick one as a programmer? Not unambiguously. Exactly. Yeah, so in this example, right, it's just the return value is often not enough to actually be able to disambiguate which one. Right? Because it depends on context. It actually gets into really deeply the context. What about this? Do you have to use the return value of functions? No. What if I just call this? Which one of these functions is going to be executed here? Hellify, though. Yeah. Exactly. And they could do something completely different. They just have the same name and the same return, different return values. I feel like that's kind of your fault as a programmer if you're like partially at least, like if you're using the same function names like that. It could be nice, though, right? You could be useful. I mean, you could think of maybe you want a function called like return ID, right? And when you call it, it's going to return, if you call it with an integer, right? It means you want that integer value of that ID. And if you call it with a character pointer, it's because you want the string value of that identifier. It could be useful. And that's actually one of the hard things about programming languages and these program language design. If you've never programmed something in this, it's hard to think of why it would be useful, right? Because you're used to thinking about your programming language and what it does, right? So if I tell you about some cool new useful feature, well, if you've never programmed in that or used it, it's hard for you to appreciate what exactly it is. Yeah. If you did do that, would it look at it just like, you know, get variables in scope and in hard scope sort of deal? How did you do that? I was like, if you actually did that. If you actually did it like this? Yeah, you have to look at the type. It actually gets into the type system. So you have to look at the type of the variable that you're using this return value as and saying like, does this type match, like is the left-hand side type going to be the return value of one of these things? Yeah. And then maybe you could do it unambiguously, right? Like this, it was just these cases. You could probably easily tell between here. But otherwise, if it's anything else, like especially with C, right, as we said, you can like implicitly cast different types of the same type. So you get into all these kind of problems about which one's actually going to be called. So this is actually why in most programming languages the function's signature. Because it depends on context and it depends on how it's used, yeah. In that situation there, could you like make your own type of namespace so you could tell if I want to use this specific one? In that case, you're just giving it a different names, right? Namespaces are just a way to give different names, right? Could you put some sort of implementation saying, okay, I want to use this? Yeah, you could do it like this, right? And then when I use it, I say infu or character foo. Right? Yeah, here you have unique names. And that's all namespaces are, right? If I had like, actually, what's called bar, right? And I had an int. Bless you. What happens if the names are not unique? I thought namespacing did take care of that. It does, but let's do this bad, right? So... Okay, you declare the namespace, right? So yeah, I'm saying that this foo is inside this namespace bar. But when I use it, right, I'm going to use bar colon colon foo, right? Yeah, yeah, yeah. But essentially, you can see here, I'm essentially changing the name, right? The namespace is just a way to give it a different type of name. Fingerprint. I mean, it's the same as this, in essence, right? It's just the difference between global and local and all that kind of stuff. Where is that name valid? Where do you use that name? It allows you to group things together a lot better. Namespaces have a lot of benefits, but the core is about the name, right? Or unambiguously specifying this function. So what's the function resolution strategy in C++ and Java? The arguments and the name, right? Well, I guess so. Actually, this gets an interesting point. Is it the number of arguments? It's the types of arguments. Right? So this gets in a more fine-grained specification about what you can and can't do. The function that has the same function or the same name would character parameters to the function. Right? So the difference is? The parameter types. Right? So one way could be just numbers and say, hey, you can't actually define the same function with the same number of parameters. Right? You could definitely create a language like this. Would it know the difference between, say, one through a day and two through a day? Is there a difference between those two? Like this? Yeah. Yeah, the return type is different. So that would be, yeah. So then you have to think, okay, so it's all about what we use. So do we use the name and the number of parameters, the name, the number of parameters, and the return value, the name, the types of the parameters, the number of parameters, and the return value? Aren't those rules set in the language? Yes. And we're talking about what are those rules, right? Because they're not. They're just design decisions done by the developer of the language, right? So we're trying to study what those are. If we were to add return type onto here, then we actually kind of get into the same problem, right, that we had with just having the return types. Now it actually depends a lot more on context, right, which is actually why most languages return type is not used as part of the signature. So most languages, right, it's not only names, it's names, the number of parameters, and the parameter types. So these kind of rules, these rules about how we resolve function names, right, and what constitutes a function name, we call them function signatures or disambiguation rules, right, if the name is the same. So in C we talked about the signatures are names only. In C++ signatures are names and parameter types, right? So here I'm kind of putting the function name in brackets here so we can see that, like, okay. So the question you kind of have to ask yourself is when I see this invocation of a function, can I unambiguously select a definition of that function? Right, so with a name it's really trivial, you just look up the name. But if we're using names and parameter types and number of parameters, right, then we have to do some additional computation. All right, so we are done. Looks like we have some other people who have this problem. Thank you.