[music] As promised, I now want to give a precise and complete definition for how we decide what code to execute when we see a method call in a object oriented programming language like Ruby. What we've seen now is there's this interesting thing, which I'll call Dynamic Dispatch. It goes by other names that essentially mean the same thing like late binding or virtual methods. And what this refers to, is the fact that when we call a method on self. Like self.m2 from some other methods say, m1. We might end up looking in code that is in some subclass of C, because the self object has a different class that uses overwriting to replace M2 with some other method. Different from the m2 that was originally defined in class c. This is the most unique characteristic of object oriented programming. The ability to do this dynamic dispatch is what makes programming with objects the most different from programming with something else like closures or just ordinary functions. So what we need to do is define this method lookup procedure very carefully, so that we can see that it has such, crisp semantics that we can fully understand it, just like we understand, the semantics of other language features, like variables and so on. So, it's worth reviewing for just a second, that the rules for how you look something up. Are often sometimes the most important rules in the semantics for a programming language. So when we studied ML we talked a lot about the environment. And we used variables and we looked them up in certain environments. Our whole discussion of lexical scope. Was really about how you look things up. Let me also point out that the rules for variables only apply to variables. So in ML, we also had field names, remember records that had fields? Those things are not variables. And we, when we ask for the foo field of some record, we, we looked up the contents of the foo field using that record. Record. So different things can have different lookup rules. You can also have different kinds of lookup rules, for example in Racket we saw multiple different kinds of let expressions. Now, in Ruby, variables work pretty much like ML and Racket. There's slightly different scope rules, blocks of lexical scope and so on. But instance variables, class variables and methods work differently. They're a little bit more like ML records field and in each case, the rules for how you look up an instance variable or a class variable or a method. All have to do with this notion of self. Which is a special thing in an object oriented language that refers to the current object. So the rest of this segment we're going to have to think about self as we define the look up rules. For these sorts of things. So let's do the easy ones first. In Ruby, there is always some object that is bound to self. We can think of that as the current object, or the self object. Whenever a method is executing, it's part of some object. That is bound to self. So when you see in some method an instance variable used like at x what we do is we take the object bound to self and we look up at x in it. It really is like the fields of a record. In this case the object bound to self is like a record. We find it. We return it. If we don't find it, we return nil. That's how instance variables work. Work. Class variables like at ax, x, work the same way except instead of looking them up in the object bound to self, we look them up in the object bound to self class. And that how all the instances of a class are sharing the class [inaudible]. Variable and it is as simple as that. Now the more interesting one and the more sophisticated one, the one that has dynamic dispatch involved is how we look up methods. When we have a method call what code do we execute. And here one slide although it's a little complicated is the answer. So here is the semantics of a method call in an oop language like ruby. So I have some call eo.m. With n other expressions. So the first thing I do in a nice eager evaluation setting, is I evaluate the n plus 1 sub-expressions. And that is going to give me n plus 1 objects. Obj 0, which is the result of easier up to obj n which is the result of e n. We'll have references to those n plus 1 objects. And as usual, this is a recursive definition, so if any of these n plus 1 objects involved self or method calls or whatever, we would complete those calls. So now we have these n plus 1 objects. Obj 0 is special, they call it the receiver of the method and it has a class because every object has a class. So let's let the class of obj 0 be c. And now what we're going to do is we're going to use c to find the code to execute. Here's how we do it. If the class c itself defined the method m, then that's the one we pick. Otherwise, we check the superclass of c, and if it defined in m That's the one we picked, otherwise we check, it's super class, it's super class and so on all the way up to top of the super class hierarchy object or basic object or whatever the last one is in the definition of. Of ruby. The first time that we find one that's the code we call. If we don't find one then we call a different method instead. It's called method missing. We follow the same recursive procedure and if no class is defined method missing well it turns out the object class does. And it's definition is to print out an error message saying there is no such method. So now we know what code to call, but now how do we call it? Well, the first step here in evaluating the body of the method is what you would expect. You evaluate the body of the method in an environment where the formal arguments to the method, the thing in the method name, Are bound to the objects obj one up through obj n. But when we're evaluating the method body, self is bound to obj zero. We evaluated the method body in an environment where self refers to the object we are calling the method on. And it turns out that simple rule in blue here properly implements dynamic dispatch. Kay? So the punchline again, which follows from the definition on the previous slide, is that to implement dynamic dispatch When you evaluate a method body you map self to the receiver, to the thing you called the method on. That way anything in M that uses self will use the receiver's class, right, which might be a subclass of the class that defined M. And that way, if the body of M. Called some other method, like M2. We will start looking for M2 in OBJ 0 class, not in the class that defined M. And this is how every object oriented programming language works. Actually in C++, it depends what kind of method definition you have, but it still has support. For this. And in most object oriented programming languages this is actually the default. And it's how all method calls work. So a couple comments on this, if you take this precise definition and you go back to our example with PolarPoint, you will see that you get exactly the right answer with these rules. That was merely an example of what I've now given you the precise semantics for. When we called dist from origin two with a polar point. Dist from origin two was defined in point, but its calls, inside its body, we still started the look up procedure with the polar point class when self was bound to an instance of polar point. The second thing i would say is I believe it's a statement of fact and not an opinion, that this rule for method calls is simply more complicated than the rule we had for closures. The rules I had a couple slides ago just had more words, more cases They had to treat self as a special thing. Different from other variables. Different from the normal sort of functional environment. And I think it's fine to say that this is a more sophisticated semantics, a more complicated semantics. Where you have variables, which are treated one way, and self, which is treated a different way. Now it may seem simpler to you if the first programming language you learned was an object oriented language. If you studied this before you studied function closures you've just had more time to get used to it. And the reason why I feel okay saying this as a statement of fact as opposed to an opinion is I'm not saying it's better or worse. I'm not saying that complicated necessarily means inferior. I'm also not saying that complicated means superior, right. I'm just saying that when you have to treat self specially different from your other things, you have a more complicated semantics for method call. Then ML and Racket had for function call, okay? So that's everything you need to know about the precise definition. Let me finish up here with one optional topic. For those of you who have seen a statically typed object-oriented language like Java or C Sharp or C++ the rules I've shown you in this segment Mint, are essentially correct. This is how Java and C Sharp work. But those languages have another complication and that is in those languages a class can have a multiple method with the same name. They just take the different methods with the same name can just take different number of arguments or even the same number of arguments, but with different types. So in Java and C, and C Plus, Plus, just because you. Have another method with the same name as the method defined in the super class, you might not be overriding. You're only overriding if you take the same arguments with the same types. Whereas in Ruby, if anytime you use the same method name, you're actually overriding. Writing. So in Java NC [unknown], you get multiple methods with the same name, either because you inherit some and define others yourself, or just because 1 class defines multiple methods with the same name. It's a little weird sounding, but they decided this was convenient. So now when you have a method call, you have to find not just a method names M, you have to pick the best one. And the rules for best take up many, many pages in the language specification, they're very complicated. Sometimes it's a tie, and there is no best one, and you get a type checking error message. So, it'd take me probably about half an hour to teach you the actual rules for this, but fundamentally. These rules are using the results of the type checker in order to know the types of the arguments in a method column. So, this sort of static overloading, which is what it's called when you have multiple methods with the same name, fundamentally does not make sense in a dynamically type language like Ruby. So, even though in Ruby and in Java, we have dynamic dispatch. Only in a language with static overloading and static types, do we have this extra complication where it makes sense to have multiple methods with the same name and the same numbers of arguments.