[MUSIC] In this segment, we're going to give types to modules. We'll call those types signatures. We'll start with the basic idea, and then, show the most important thing signatures are used for, which is to hide things inside of modules from all the code outside the module. So, I really do want you to think of a signature as a type for a module, but the same way we call modular structures, we will call their types signatures, we're just using different english words. And what a signature indicates about a module, is what bindings are defined in there and what type do they have. So, we can define a signature, actually, separately from any particular module, and then, we can say that module has the signature. So let's see how this works with the example we used in the previous segment. So we can define a signature, as you see here at the top, we use the keyword signature. It's somewhat conventional to use all capital letters. You don't have to do that. Say this is the MATHLIB signature, then, sig, which is a key word, although, it's not an English word. The types of bindings just like we've been seeing the RPL print out, so we would write val fact:int->int to say any module with the MATHLIB signature must have a variable fact of type int->int, however it's defined. It doesn't matter if it's defined via a function binding, or a variable binding or any other way. The point is that it has such a thing that it defines, similarly half_pi, and doubler, and then the keyword end. Now, our structure that we defined in the previous segment, my MATHLIB, could have this signature. It defines all the things that the MATHLIB signature requires. And in such a situation you can write :> and the name of the signature in your structure definition between the structure name and the =. And now, the structure will only type check if you provide everything at an appropriate type. So if this signature had something that the structure did not provide, we would get a nice error message from the type checker reminding us that we didn't provide it, and similarly, if we provided something, but not at the correct type. Okay? so, in general, the way we do signatures, is you just write signature, name of your signature, equal sig, the types for your bindings very much like the REPL has been printing things out for us in N. And in fact, one way you can get a start to your signature is paste in what the REPL says a structure has. in this signature, you can have things for a variable, types data type and exceptions, we'll see examples of that in future segment and then when you want to ascribe a ignature. to a structure, you do it like this. So the advantage of having this, as we will see, so we can define a signature once, and then, have different structures in our program that all have that signature and we can could reuse the signature name. And as I mentioned the module simply won't type-check unless it has everything at the right types. So if we see that over in our file here, I have the same structure I had before, but now I have the signature as you've seen from the, on the slides, then my structure, and then, later here, in the file, the same uses, and if I load all this up in the rebel we now see that it defined the signature. Having done so, all it's going to tell us about the structure of my MATHLIB is that it has that signature. And then we have pi and 28 and everything works great. Alright? So that's the basics of signatures, but now let me show you what they're actually good for and that is hi-, hiding things. The real value of signatures is not to document and write down the type of everything in the module. It's to hide implementation details, which is the most important strategy for writing correct, robust, reusible software, is to say to the outside world, I don't want you using eveything defined in this module. I want control over what's public and what's private, what you can use, and what you should not assume even exists. So we actually have ways to do this already in ML, whether it's with local helper functions or what not. you know, here are three versions of a double function and there are all sorts of reasons why clients of this function have no idea how it's implemented. It doesn't matter whether you write x*2 or x+x or x*y where y happens to be equal to two where the function is defined. We also know that if we define helper functions locally, no one outside of the let expression where the function is defined, even knows that function exist. What module systems are giving us, in addition to other things, is the ability to do this for a sequence of bindings at the top level of the module. It would be really convenient to have some functions that are private and some are public. In a lot of programming languages, we do this by actually marking the the functions, private or public. In ML, we do it a little differently. It's nice to see something different. what we do in ML, is we just write our module how we want, and then in the signature, we leave things out and anything not in the signature cannot be used outside the module. So here's how that works. If I just go back to my example, and I take out one of the bindings from the signature, the module, the structure can still have that type. The module is allowed to have things not in the signature, it just has to have everything that is in the signature. And whatever is not in the signature cannot be used outside the module, it can still be used inside the module. So let me show that over here with the code. So let me go back up here, and let me comment out from my signature, the doubler function, so no one on the outside can know that doubler exists. but I can still use it inside, so I could have a value eight here that would be a doubler of four, that would work just fine. And, then, if I go over here and recompile everything, I'm actually going to get an error message. And, we will see that the error message is, on line 27 there is an unbound variable, MyMathLib.doubler, and indeed there is. Right here on line 28, there is no such thing as MyMathLib.doubler outside of the module and so I simply cannot use it. Alright? So having fixed that, everything should now work. I still have my val pi, I still have my structure, MyMathLib that has the signature, MATHLIB. Okay. So that is hiding for us, and that is one of the ways we are going to use modules to hide things from the rest of the program. We'll see that signatures have even more power that will let us code up neat things in the upcoming segment.