In this segment we start our study of data types. This is the language concept we'll use in ML to make our own one of types which is a really important feature in programming. but ML does it in a very different way than other languages you've probably seen. So it'll take us a while to build up the ideas we need. So what we're going to do is we're going to introduce our third kind of binding. So far we've seen variable bindings with val, function bindings with fun, now data type bindings which start with the keyword data type. These pack a lot more punch into one binding, so I have to tell you the different things that happen when you use a data-type binding. So, what you do here, as you see on the slide, is you write some type name, any name you want like MI type, is just a silly example to show you the pieces. Then an equals, then a bunch of possibilities. These are separated by the pipe character, which you can think of as or. So, this says, I want a new type Mytype. And the way you make values of MyType is either, they carry an int, star int, or they carry a string, or they carry nothing. So they are one of types. Every value of MyType is either going to have a pair of int's or a string or nothing. And we could have any number of possibilities that we want. Now, what I haven't explained yet are these things you see in the capital letters: TwoInts, and Str, and Pizza. I chose pizza just to emphasize that there's, you, these can be any names that you want. It's conventional to capitalize them. Some people use all capitals. but I'll just capitalize them like you see here. and these we call the constructors. So what is happening when you introduce a data type binding, is that we are adding multiple things to our environment, both our static environment, and our dynamic environment. We're introducing this new type name, mytype. And we're introducing the constructors, TwoInts, STIR, and Pizza. And these constructors have a couple purposes. But for now, they're just functions that given values of the correct arguments, return something of MyType. So two ints, as a constructor is now in our environment and is a function of type int STR int arrow MyType. If you give it a pair of ints you will get back a MyType. STR is a function from string to MyType. And pizza is not a function because it doesn't carry anything. It already is a value of type MyType. Alright. So, lets try this out in the repple I have the same example here. Here's a data type binding. And then I just have a bunch of variable bindings that are using the constructors in various ways. So let's just load this up and use it, and see if what we get back makes a little bit of sense. Alright? So, what the repple prints out is first, it evaluated the data type binding. And it'll just print the whole thing because it is relevant to what we see in the rest of the file, in the rest of our program that we now have a type MyType and it has constructors, pizza, STR which takes a string and two ints of in star int. The order of constructors in our binding doesn't matter. So as usual, the repple has chosen to alphabetize them for us just like it does with record field names. Then when I said val A equals STR high, that makes sense. STR as a constructor, is a function from type string error MyType So, if we call it with a string, we get back a MyType and that's why you see here in the repple that val A does, indeed, have MyType But what's the value? Well it turns it is STR of high. These constructor if you can also think of as tags as things that are actually there in the values to say what kind of MyType we have. Here we have the STR kind of MyType and the underlying value, the underlying string, is the string high. So, that's why A is bound to the value, STR of high. So, constructor applied to value is the value. Alright. If you had just said STR without an argument well, that is just a function from string to MyType that's why valve B here is a function from string to MyType. This is a common programming error. You probably didn't mean that. You probably meant STR applied to something. But it does type check, and it just does something that you probably weren't expecting. On the other hand, with pizza, that makes perfect sense. Pizza is a value of type mytype. So, if I have that as my expression for C, then C is going to be pizza. The same way A will be STR of high. Alright, D similarly, if you want to use the two ints constructor, we'll pass it in int STR int. So, in this case, we'd evaluate one plus two to three, three plus four to seven, pass that resulting pair to two ints. And so, the result is this value, two ints of three comma seven. All right. Once you have these values you can use them however you want. So if you say val E equals A, of course we look up A in the dynamic environment. We get STR of high and that is now the result of E as well. Just like it has been with everything else, that we've seen in the course. All right. So there are a few examples. Let's go back to the slides and consider even some more. Okay, so anytime you have a value of type my type, its going to be made from one of the constructors. That's why it's a one of type. And the value you get back really does have two parts. It has what I'll call the tag part, that remembers which constructor you used to make this value. And then it has the corresponding data. So for example, if you this expression, two ints three plus four, five plus four, that evaluates to something with tag part two ints And corresponding data part, seven comma nine. Similarly STR if true then hi, else, bye. would evaluate to STR of hi. Okay. So we now know how to build data type values. But whenever we introduce a new type in our language we need a way to build the pieces and we need a way to access them. For one of types and data types, like mytype really are one of those types. There are two aspects to accessing the value. We need some way to check which kind of thing we have, which variant of mytype we have, ie, what's the tag? Which constructor made it? And then we also need a way to get the underlying data, if there is any. For pizza there isn't, but for two ints and STR there is. So this is a moment where it is worth looking at the other one of types we've seen, lists and options and seeing that they have these two aspects for accessing the values also. Null which tells you whether a list is empty or not and esum which tells you whether an option issome or none are the variant checking functions. All right. That's all they do. They tell you which tag is it the empty list or is it not the empty list. Is it none or is it some? Where as the head, tail, and val of functions we've seen, those extract the data. Head returns part of a list. Tail returns part of a list. Valid returns the thing under the sun. Now we have an issue here. What if you apply head to the empty list, or val of to none, well those raise exceptions for the wrong variant. So now that we have data type bindings. One thing ML could have done is said that when you introduce a data type binding, like data type, mytype, equals, all that stuff, in addition to getting the constructors, those functions two ints and STR in the constant pizza, you would also get functions for checking variants and extracting data. Maybe it would add to the environment functions like is STR of type MyType arrow bool, take it to MyType and return true if that value was made from the STR constructor. And get STR data, of type mytype arrow string, that would get you the underlying value, if it is a STR and raise an exception otherwise. These would be direct analogies of how head and null work. Null is like isStr, head is like getStrData Alright? That would make sense. It would be a perfectly reasonable language. It would probably be easier to teach you right now, but instead ML did something much better, and we'll start studying it in the next segment.