[MUSIC] In the spirit of showing the complementary benefits of different approaches, I want to finish by comparing generics, the the type variables we saw in ML, with subtyping. This is right up there in the course with comparing functional decomposition, with OL decomposition or static typing versus dynamic typing. And what we'll see is that generics and subtyping are simply good at different things. So let's start with what generics are good for. We saw this in ML, so I'll present it in terms of ML. They're great for functions that combine other functions. For example, we were able to write the compose function in ML and give it this beautiful type. That's why they can take any function of type 'b -> 'c, and any function of type 'a -> 'b, and it will return something that has type 'a -> 'c. And these types can be instantiated with any type in our language. It describes precisely what compose is doing. We also saw that generics are great for functions that operate over generic collections. That a length function for lists can work for any type of list. That the map function has type alpha arrow beta, an alpha list to beta list. It doesn't care what the list elements are, provided that the argument type of the function you pass expects that type. So in general, we saw many other idioms, generics are great when you have code that can work for any type of thing. But some of the things have to be the same type, which is when you see 'a or 'b appear multiple times in a type, it expresses those type equalities. So generics are great not just in functional programming, that's why Java and C# also have generics. They tend to be a little clumsier to use because there isn't as much type inference. They're often are little more difficult semantically because of how they were added later to the language, and how they interact with OOP and objects. But nonetheless, we even saw in an earlier optional lecture how to code up things as fancy as closures or manual effective closures in Java. And it's not so bad, and that's why people in Java and C# do use generic types in al the situations that they're useful in ML where we saw them there. Now, we didn't study Java, so you're not responsible for this syntax, which no one tends to love. But here is a very simple pair class in ML that even has a swap method. And this class works for any type of things that go in the first component and in the second component, which I've called x and y here, okay? So you do not want to use subtyping in these situations that I've remarked here generics are good at. That if you try to use subtyping for things like a container, like a pair, you just end up using the wrong thing everywhere. What should the type of the fields of a pair be if you don't have generics? All right, if you have to pick one type for all pairs, well, I guess you would have to use something like object. And that's what people did in Java back before Java had generics. And it's really unfortunate. It's really using the wrong programing languages tool for the job, which is I guess what you have to do if you don't have the right tool. Now we have the right tool in most of our statically typed languages and you should use it. What you end up doing is you end up putting on your fields the components of your pair, that it has type object. And thanks to subtyping, that's fine when you write to the fields, when you create a pair. You want to pass in a string? No problem, string is a subtype of object. You want to pass in a color point? No problem. Color point is a subtype of object. But then when you get the fields back out, and let's be honest, the only point of having a pair is to get it out at some point, right? When you get it back out, all you know is that it has type object. So there is nothing you can with it until you somehow get back that it has some other type. And in Java, and C#, we do that with a downcast. This is basically a runtime check, that says, I actually think it's a string, and if I'm right, please give me back of something of type string, and, if I'm wrong, raise an exception. So by having to put these downcasts in, we're not getting static checking, those tests could fail, and we're paying the runtime cost of actually doing the check, and we're making our code harder to read. So it's kind of a lose-lose-lose compared to generics, and that's because generics are better for this kind of programming. Subtyping is great for other things. There's some great uses of subtyping, which is sometimes, in fancier verbiage, called subtype polymorphism, right? Whenever you have some code that needs an object that behaves like a foo, and you have some object that has more than a foo needs, you have some subclass of foo that has a bunch of extra stuff, no problem. It's great to be able to pass that in. Generics don't have that idea, generics are about, I work for any type. This is about saying no, I need a foo but if you have a subtype of foo, no problem. So where does that come up? As I've shown in this section repeatedly, there are many simple examples, like something needs a point but you have a color point. That color field is no concern to anyone. Subtyping lets us capture that idea and generics don't. The other canonical example where I think object oriented programing has just been very, very successful is in programming graphical user interfaces. Okay, so you have some super class which has ideas in it, there's some super type that says anything of this type has the ability to be on the screen to respond to mouse clicks, to be resized. And some code can operate over anything with that type, but presumably, the things actually being passed have much, much more, like a color, and a default font and all sorts of menu bars and all that sort of stuff. And subtyping feels like the right thing there. That you have some code that only needs some things and then you have some objects that have some more things, subtyping's great for that. And if you try to do even simple things like points and color-points in ML, it's frustrating. Now, nothing's impossible, there's always some way to code it up if you're willing to go through enough workarounds. But ML really does not have subtyping. So if you try to write in ML, this is actual ML code here, a distance to the origin function, that takes in a record with an x field and a y field, distance origin works just fine. But its type is record with x of type real y of type real to real. And if you've tried to call it with something that has extra fields, it just doesn't type check. The types are not equal, ML does not have subtypes, it would make type inference more complicated and ML chose not to have it. So you cannot call distToOrigin with this record. Now what you could do if you're interested in a workaround, the same way in Java, pre-generics, there were ways to do collections, they were just cumbersome. Is you could change distToOrigin to not take an x:real, y:real at all. To actually take anything of type alpha, but make the caller pass in a getter for the x coordinate and a getter for the y coordinate. And if you do that, then what you end up with is a function that has type alpha arrow reel and alpha arrow reel and alpha to reel. Make the caller pass in how to get the x coordinate and the y coordinate, and then the actual v here can be anything you want. And that's frustrating if most of your code just wants to do this with plain old points, it would have to create a helper function that had the getter, the right get x and get y. But this code could actually be used with something that was a color point, provided we passed in the appropriate getters. And yet, because of the lack of subtyping in ML, you would actually have to use different getters and setters for points and for color points. You really cannot reuse code in ML for points and color points, and that's because subtyping, which ML does not have, is the right tool for this task.