[MUSIC] This segment doesn't have any material you need for the homework or the exam, but it really helps crystallize what dynamic dispatch is. So I hope you'll follow along and at least get the high level idea. What I want to do, is write Racket code that behaves like there's dynamic dispatch. That codes up manually in dynamic dispatch. We're not going to use anything in Racket that has dynamic dispatch given to us as the default. Now Racket actually does have classes and objects. So this is not something you would actually do in Racket. I'm doing this for a different purpose. I'm doing this to demonstrate how the semantics of OOP, the semantics of one programming language can be coded up in another programming language. I always find it helpful to understand the semantics of something by seeing how you could implement it using other constructs. So we've actually done this sort of thing before in the course, at least in optional segments, for example when we saw closures in ML. I had some optional material on how you could code up closure-like things in Java or C. So now I'm really kinda doing the opposite to show that these languages are not as different as you might think. So there are many ways we might do this. I'm gonna show you a bunch of code in this segment. So let me just jump in to how I'm going to do it. Of course there could be other ways. I'm going to use a struct in Racket for objects and I'm going to say that every object has two things. It has a list of fields and a list of methods. And that's all an object is. I won't have any notion of classes, so this won't be quite how Ruby does OOP, but I will get dynamic dispatch even without classes and that's interesting too. So both fields and methods are going to be lists. The fields are a list of mutable pairs, so that I can update the contents of a field, so I can set them. And the methods will be a list of immutable pairs, because the way I'm doing this, once we have a method in our object we're not gonna be able to update that to a different method. Of course you can make other design decisions. So for the fields, it's just gonna be an mcons of some symbol, what's the name of the field, and then some value, that'll be the current contents of the field. For methods, they will also have a name, like a get-x method for a getter, for the x field of a point object. And then, the other part, is a function, but it's a function that takes one more argument than would otherwise need. And this is the key trick. It takes an extra argument where our implementation that is simulating dynamic dispatch will pass the appropriate object for that extra argument. So i'm calling it self to remind us that this is the current object from OOP but this is not special in Racket, this is just a variable name that I happened to call self. I could've called it s, or foo, or x, or anything else. Okay, so this is not a particularly efficient implementation, using lists for fields and methods is much less efficient than a vector or some sort of hash table. It's not class based, but it lets us focus on this key issue which is this extra argument self. So you'll probably get a better picture of what's going on if I literally show you a picture. So this is a picture of what a point object bound to a bracket variable x might look like. So, x would refer to some object that you see here in the middle, that just has two fields, one called fields and one called methods. So fields is a list where the elements in the list are mconses, mutable pairs. So for example, maybe you have an x field that holds minus four, and a y field that holds zero. If you had more fields, this would be a longer list. That's all there is to it. The methods is also a list of pairs. And so for each of those pairs, you see here, get-x, set-x, it would be several more and you see distToOrigin, would be the name of the method and then a Racket function. A Racket lambda that takes one more argument than you would expect, so both self and args, and that's the idea. So now let me flip over to the code, and basically show you just a few Racket functions that use these objects to implement object-oriented programming. So here I have the struct definition, just like you would expect. I need one helper function that I couldn't find in Racket's standard library, maybe it's in there. It's like a soak, which we have seen before, takes a list of pairs, compares something against the car of those pairs, the first time it finds something, it returns the pair. But I want this for an immutable list of mutable picks. So something like my field's list and I use that right down here. But now lets get to the three main functions get, set and send. So, get is a function that takes an object and a field, so its a racket function, and returns the current contents of that field in that object. So all I do is get the fields of the object. Call that assoc like helper function I just defined. That should give me back a pair. If I actually get a pair, return its putter. Otherwise, I didn't find the field, I got false instead. And so I just ask for the field of an object, it doesn't have that field and I chose to raise an error, rather then Ruby's approach of returning null. Let's set a field. It's just gonna be a Racket function that takes an object, a field, and a new value for that field. Do the same computation to find the appropriate pair in our list of fields. If we find it, you set and mcdr to update it, otherwise, say we didn't find it. And now the interesting one, let's call a method which we know is also called, sometimes, sending a message. So, here's a Racket function that sends the object obj, the message msg with the args args. I'm doing a little convenient Racket thing here for multi-argument functions, you can ignore that for the high level idea. But now what we do is, we get the list of methods out of the object. Use a soak to get the appropriate lambda out. So this will be a pair of the name of the method we're looking and the lambda that implements that method. So if we don't find it, raise an error message as before. If we do, the cdr is the lambda, and let's call it yes with the args, but as it's first argument, the entire object itself. What we're doing here in this send helper method, is passing into the lambda as the first special self argument the entire object. So let me show you back on the slides why that works. So I have the same object I had before, and now suppose someone wants to send to that object the distToOrigin method, the message. So it doesn't take any extra arguments, so you would just write this. Take this object, call its distToOrigin method. So, what send will do, the code I just showed you, is get the list of methods, use a soak to find the right pair, so that will be this pair right here, distToOrigin. Then take the cdr of that pair, which is this lambda, and call it with self being the entire object, being x. So, in the body of this lambda, anytime it uses self, self will be bound to the entire object, and that's exactly what we need for dynamic dispatch. So, back to the code. What have I shown you so far? Nothing except a struct definition which is nothing to that. get for getting a field, set for setting a field, and then the key thing of sending where the entire action is because our methods take an extra argument I can pass in that object right there. So now let's see how I could use these things to make objects. So here's a Racket function, it's just called make-point. When you call it with two things, presumably numbers, _x and _y, it creates an object by calling the object constructor. We can see that right here. So when you call the object constructor, you have to pass in two things, a list of fields and a list of methods. Here's a nice list of fields. We say the x field is initialized to the contents of the racket variable _x, the y field is initialized to the contents of the racket variable _y. Our methods list is a little bit longer. Here it is, okay. And it's a list where we for each of them give a pair of the name of the method and then a lambda. And the lambda always takes this extra argument. And in fact, this get-x method uses dynamic dispatch because when you call this lambda, we get self and we don't need any of the other arguments and so we then use our get method, which is on self with _x. So excuse me, this is not dynamic dispatch cuz it's just getting a field, but distToOrigin is definitely using dynamic dispatch. So, here I'm going to add a method, called distToOrigin. I'm going to use this lambda, and this lambda when you call it, will send self the get-x message, and by sending self the get-x message, we will get dynamic dispatch. So, I can show you a use of make-point. This is just this function here that's acting like a constructor for point objects, kind of like an initialize method. If I call it right here, with -4 and 0, I'll get back a point, with all those fields and methods, with an x coordinate of -4 and a y coordinate of 0. So if I send it the get-x message, I'll get -4. If I send it the get-y message, I'll get 0. If I send it the distToOrigin method, I'll get 4. I could then set it's y field to 3. And then I could ask its distance to the origin again, and I would get 5. Turns out -4, 3 is distance 5 from the origin, it's a Pythagorean triangle. And if you look at this code, except for some slightly strange syntax, it looks like object oriented programming. Create a new object with make-point. Read, call these methods to get fields. Call these other methods to get results. Call a setter method and so on. So I'd argue this really is object oriented programming, especially if I can get sub classing to work. So in the code here, I do have a color-point class, but that's not the interesting one, so I'll let you study that on your own. Instead, I'll jump straight to our fancy one which is making a polar-point. So a polar-point takes in an _r and a _theta, we saw a similar thing in Ruby. And what I'm going to do here, is I'm going to create an object, here's my object constructor, that uses the field and methods of point, but then adds extra things on the front. So for the fields I'm gonna take whatever fields you would get from making a point, as I do up here, and then add on to the front using a pen, an r field and a theta field. For the methods, I'm going to do the same thing. So, I'm gonna start with the methods in point, then I'm going to append on all these other things. And some of these methods are new, and so I'm adding them to my list, and when I look them up, I will find them. Other ones like get-x are doing the moral equivalent of overwriting. And by putting them closer to the beginning of the list, I will find them first, because the way a soak works when it goes to look things up, is it starts at the beginning of the list. So, simply by putting them earlier in the list, they will override anything that was in the list I got from the methods for point. And as you might imagine, the key thing I do here is, I override the getter for x, the getter for y, the setter for x, and the setter for y, to compute in terms of r and theta instead of in terms of the fields x and y. And thanks to the dynamic dispatch, the distoOrigin lambda defined up here in make-point, when it now sends to self the get-x and get-y message on something created by make-polar-point, we will do the right thing and get the right value. And I have an example down here. If I make a polar-point with an r of 4 and a theta of pi, then I will have something that should behave like when I made an ordinary point with -4 and 0, cuz an angle of pi gives you a y coordinate of 0. And I have the same sequence of message sends here, and I should get roughly the same answer. Let me show you that quickly. If I just run this, you actually get three objects I played around with here. This first one is the plain old point and I end up seeing that after I set y to 3 and then that's the difference the origin is now 5. The second one uses a color-point, which I didn't talk about here in the video, but you can look at on your own, and this third one is where I had a polar-point. And it actually is right when I ask for get-x I get -4. Even though the r is 4, the x coordinate is -4 because that's how polar-points work in this case. get-y should be 0 and it really is. This is just a rounding error. It's giving me 3 times 10 to the minus 10th power. That's a very small number. Let's call it 0 and declare victory. And the distance to the origin is 4. After I set-y to 3, when I ask the distance to the origin again I get 5, so it really is perfect. Okay, back to the slides just to wrap up, I know this has gotten a little long. This is the idea of subclassing we saw, that because, as you see here in the blue, when we send a message to self, self ends up bound to the entire object because of that send function we wrote. It's the send function that binds self to the right thing to implement dynamic dispatch. And finally one last thing, now that I've shown you all this, some what clever somewhat illustrative Racket code, is I was very careful not to try this exercise in ML. ML's type system would really get in our way here. The lack of subtyping makes it very difficult to create a polar-point object that can use things that assume their given points. That the type you would want to give to the function that we're using for distoOrigin, is not a type that you can conveniently write down in ML. So we were quite lucky that in Racket we did not have a type system to get in our way. Now there are ways to do this in ML. For example, you could give all objects the same type using a big datatype constructor. I don't want to say it's impossible in ML, but I do want to say that ML's type system is not friendly to coding up your own dynamic dispatch, which is why ML-like languages that want to support objects, like OCaml and F#, add objects as their own thing in the language. And Scala does the same thing as well. So it's fair to say that ML, as we saw in this course, is not friendly to coding up your own dynamic dispatch. But it's also fair to say that a language that was focused primarily on types for objects, like Java before Java added generics in the version five of the language, are equally hostile, if you will, to the sort of generic programming and polymorphic code with function closures that we saw in ML. So it's a good example, where sometimes type systems prevent switching programming styles but still support a particular programming style very well.