[MUSIC] What I'd like to do now is compare how dynamic dispatch works with how closures work. Because there's a fundamental difference that exposes a key trade-off in how we should design software and the semantics of our programming languages. So, let me actually show you some ML code that uses closures. It's a contrived example but, it's short and it demonstrates what I want to demonstrate. So, suppose I have two mutually recursive functions, even and odd They're mutually recursive because even, and these both only work for non negative numbers by they way. If x is 0, then even is true, else it calls odd with x-1 and odd takes the number x, and in fact it's 0 that's false, otherwise it calls even with x-1. And because I want to really see how these are using each other when we call them. How about I add in some print statements like in even and then just a semicolon and then return. And similarly with odd, I'll write print in odd. So that we can just see when each of these functions get called. So as you might imagine, if I call odd with 7 as I do right here. It's going to print in odd, in even, in odd, in even. Probably 8 times I would guess and then return the appropriate value which is true. Okay, we'll see that. We evaluate A1. A1 will end up true and will print 8 times. But now supposed we shadow even. So down here, we have a different function, even that does a much quicker algorithm right. This one just takes x, divides it by 2 and sees if x mod 2 is 0. This is a constant time operation rather than the silly recursion up here. Well, now if here we call odd, absolutely nothing will be different. Because even though we shadowed even for the rest of these file, when we evaluate this call to odd we will evaluate in the environment where odd was defined. So odd will, when it gets to this call even, call this even up here at the top. Even will call odd, odd will call even and we should see no difference. And you might look at that and say well that's a bummer, right? Maybe it would be nice if when someone comes up with a better definition of even. It would improve the definition of odd even, if the better even writer did not know that odd was using even. On the other hand and this is the trade off, if someone decided to shadow the earlier even with this third version. That just does something completely different, just always returns false. Or some other things that maybe works for them but it's not what we needed. Then it's actually a very good thing that this call to odd for a3 will be completely unaffected by the fact that there's a different definition of even here. So, in general what's going on here is the recursion with even and odd is closed. Once we create a closure which even sounds like closed, we're done. And you can shadow odd. But if you call this function here, it will always behave the same way. Because closures use lexical skill. So without further ado, let's run it. I've got everything keyed up here. All I need to do is hit return to use this. And then let's see what happened. So sure enough, all three times there were three calls to odd. I saw in odd, in even, in odd, in even, in odd, in even, in odd, in even. 8 printings like I expected. And then in all three cases, a1 is true a2 is true and a3 is true, okay? So that's just a blast from the past with ML, but now let's see some similar code in Ruby, and how it behaves completely differently when we use dynamic dispatch. So here's some Ruby code. I have a class A. This class A has two methods, even and odd. And they use the same silly mutually recursive feature. They each print something and then in even if x==0 then true, else self.odd(x-1) Right? A method called to the odd method, and similarly the odd method calls the even method. So these are just 2 methods in a class, So if we did something like make a new instance of the class A and call its odd method was 7. We would see 8 printings, in even in odd, in even, in odd. And then here I have a little thing to print out, the result here that a1 is, and I would hope to see the answer of true. Right, that indeed 7 is odd. Same as the previous code. Now, let's do sub classing and overriding which we know has dynamic dispatch. So here's a class B, which you can see is a sub class of A. And here I override even, to be this faster algorithm. And what this will do, is even though class B inherits Class A's definition of odd. Thanks to dynamic dispatch, when an instance of B has a call to odd that calls even, it will be this even, because that's what dynamic dispatch is all about. So, when we do this, if we have a B.new, like you see down here with this a2. You will actually see that we'll get the correct answer and it will be faster. This is only going to print out one thing. Right, because we call odd once. It prints maybe two things it calls even and then we are immediately done. Okay, on the other hand if you have a different sub class here, C. That has an even, that does the wrong thing, that just always evaluates the false. Then when we make a C object and call its odd method, it will also have dynamic dispatch, it will also use this even method, and we'll just get the wrong answer. So let me show this, just to show you that I'm telling the truth. It's all ready here, let's just load it. And sure enough with a1, we ended up doing lots of printing, that was the normal way. And then a1 was true. With a2, that was in the subclass B with dynamic dispatch. We called odd, which we inherited from class A. But when it called self.even, thanks to dynamic dispatch, we ended up in our new definition of even and that's why we immediately return true. And then for the version in C, we, again, have dynamic dispatch but that's a problem. Because in that third version, that means we actually end up getting the wrong answer. Because odd called a version of even it was not expecting and does not do what odd needs in order to get the right answer. So that's a code demonstration. Why am I showing this to you. Because I think this is an essential trade off when you're using subclassing and overriding. See, if you have a method in some class that makes calls to other methods that can be overridden, then the behavior of the method can change in subclasses. Now, maybe that's on purpose, like in class B, and maybe that's by accident, or not what you want, like in class C. But either way the observable behavior of this method you're defining, includes the fact that it calls methods that might be over written. So this is both a problem and an opportunity. It's a problem because it makes it much harder to reason about the code you are looking at. In ML our closures were closed, I could look at the definition of odd and know how it would behave. With the object oriented version with dynamic dispatch I had to say well and then it calls an even method which might change in a subclass. So in an object oriented programming language, this is not what you want. If you want to reason about code in isolation, you should somehow disable overriding or not call methods that can be overwritten. There are various ways to do this in languages. In Ruby we can make methods private. Right. And that might help because then subclasses cannot use them even. There are also ways in languages like Java to mark a method final. Which is a special keyword that says, you cannot override this. And whether that's good or bad style is often debated. Because it is less object oriented to disallow overriding. But it makes it easier to reason about code isolation. And in some ways, makes things more modular. Now what's good about dynamic dispatch and the ability to override things is that a subclass can affect behavior without having to copy code. Right, we were able to override the even method and have it affect the odd method. In ways that even the original writer of odd was not anticipating. So this can often let us do things without having to modify code that was provided by others in libraries. But it's rather brittle, right? The fact that we could change odd by overwriting even relies on some details of how odd is written. It might stop working if someone modified the odd method, but it does allow more code reuse and whether that's code reuse or code abuse depends on the situation. Nonetheless, semantically, as I've emphasized here, closures and objects are fundamentally different. And the difference is dynamic dispatch, which is why I consider dynamic dispatch to be the most novel thing about object-oriented programming.