[MUSIC] In this segment, I want to talk a little bit about type inference and then, we'll continue this discussion the next segment about one type being more general than another. We're going to study type inference a little later in the course and understand how ML actually does it, but I need to tell you a little bit about it now so that you're not concerned on your next homework if you're getting some types back from ML that might surprise you in certain ways. So, as I mentioned, once we learned how to pattern match on each of types, in the second homework, we don't want you to use the hash character. We don't want you to say hash two or hash foo to get the foo field out of a record. And conversely, now in ML, once we stop using the hash character, we don't have to write down types on any of our function arguments anymore or on any of our variable bindings or anything else. And it turns out these things are related. That once you use pattern matching for getting the pieces of a tuple or a record, the type checker can always figure out what the type of a function argument should be. So, let me show you how that works mostly by showing you code. Here are two functions we've seen before. Our old friends for summing a triple. Take in a single triple x, y, z to pattern-matching against the three parts and sum them up. Or take in a record here, with first, middle, and last fields, bind those fields x, y, and z and then concatenate them together with some blank spaces in between. So, if I just run this and, load this up we will see, as we've seen before, that the type checker can figure out that sum_triple1 needs to have type int * int * int arrow int. You can tell it's a triple, because it's x, y, z. You can tell they have to be ints because x, y, and z are added together. Similarly, full name has to be a record with first, last, and middle fields. And those contents have to be strings because in the body, we concatenate them. And then, the result type of full name one is of type string. So, these patterns told the type checker most of what it needed to know. And then how the variables were used showed the rest. So, let me show you that if you use instead hash characters, the type checker does not have as much information. Now, these versions, sum_triple2 and full_name2, which use hash characters the old fashioned way, will work, and they will work just fine. So, let me quickly go over here, and just show you that, that if I reuse them, they work just fine and we get the correct types for sum_triple2 and full_name2 but if I took out these types, and let me just do this for the first one here and try that again, I now get a compilation error message, unresolved flex record, need to know the names of all the fields, blah, blah, blah. And the reason why the ML type checker is complaining is it looks at this sum_triple, and it can tell the triple here needs to be a tupple with at least three fields where the contents are type int. But how does it know that there isn't a fourth position, or a fifth position, or a sixth position? It doesn't. And every type system has certain limitations. And in ML, one of those limitations, is you can't write a function that takes on both three tupples and four tupples. And so, since the type checker can't figure out how wide the tupple supposed be, you get an error message. And that's why this simply doesn't type check and why, in homework 1, we made you write those types down. Alright. So, that's fine. That's an advantage of not using hash. But now, let me show you something that you may trip across in homework 2 and so its important that we tell you about it, okay? So, here are a couple of functions that don't use all the pieces that were pattern-matching against. So this function partial sum adds together x and z and doesn't use y. And, similarly, this name function concatenates the first and last name but doesn't use the middle name, alright? So, if told you on your homework, say, to write a function of type int * int * int arrow int, that indeed had the behavior of adding the first position in the third position, you would probably write exactly this code. And then, you would save it. And then, you would come over here and you would run this. And you would see that the type of partial sum is int * alpha * int arrow int. And you would say, oh no, I wanted an int * int * int arrow int, and what I got was an int * alpha * int arrow int. And what I want to tell you is, that's okay. That sometimes, the type checker is perhaps a little bit smarter than you are, and in looking at your functions, decides they are more general, more reusable than you expected. And indeed, if you look at this, it worked just fine to call partial sum with three integers. It's a polymorphic function that works for a tuple where the middle position has any type, and indeed, we can call partial sum with 3, 4, 5 and get eight, but we could also call it with a string in there, and that will also type check. It would not work to put that string in the last position, alright? That doesn't type check, and that's because if you look at the code, the last part does have to be an int, because we add it. So, whenever this happens, and it often happens when you're not using part of your argument, you'll end up with a more general type than you might expect. And that is okay, alright? I sometimes call this unexpected polymorphism, alright? If your function is correct, and you still need to test it, you still need to make sure that it does work for the types that it is required to work for. Then, if it also works for values of other types, well, that's just a useful thing. And we don't need to worry about the type that ML gave us as long as it's more general than what we needed. Now, hopefully this idea of more general is intriguing to you, and you'd like to learn, hey, given two types, how can I tell if one is more general than another? And I can give you precise rules for doing that and I'll do it in the next segment.