Using signatures
Contents:
# Signatures
OCaml has something called signatures. These can be thought of as type definitions for a module as we literally start with module type. Let’s define a signature for the factorial function:
=
So, yes even though I was spekaing about it in the sense of a module, its really applicable to functions as well. Here, we’re saying that there exists a value called fact which has the type int->int.
The val keyword is pretty important, we see it in utop all the time. Don’t try to use it in your programs as variable names!
Now we can define Modules which have this type:
=
This is just for the type checker to check that all the types work out correctly and for the programmer to ensure that they make lesser mistakes as the compiler will catch it!
The
module typeis where we go to read the documentation for what a certain module is supposed to be doing. Its the public facing documentation for the world to see. You know where we’re going with this? (shhh….mlifiles).If in the module Factorial, we changed the name of the function to say
factorial, it will throw an error saying that it expected to findfactbut its not there.
The cool thing is, if say you write something like this:
=
This is a valid way of writing the tail recursive function. Now if someone wants to access the fact function, they can do so like this:
let a = fact 10
But what they cannot do is this:
let a = aux 10 1
Because we have not made the aux function accessible via the type definition of Fact. Hence, if we want users to access the aux function as well, we’ll write:
=
# Describing our stacks
Porting code from the older notes:
=
=
Now let’s define these in the interface so that the public can use it:
=
Typically we would write more comments to explain what each of these do but its fine for now. So, this stack signature will fit the ListStack definition very well, but what about MyStack? The signature we have written is looking for 'a list everywhere, which means its not general enough. Let’s generalize this:
=
Therefore, we can now write:
=
Let’s also create these signatures for queues:
=
Okay, now the thing is, if you want to apply this to your modules, and you had defined your modules before, you could also do a rebinding like this:
= listQueueImp
Since this is also immutable, we’ve simply just created a new memory space and allocated the old module to it with a new name and more importantly, new type.
# Semantics
- You can specify
val,type,exceptionand even othermoduleinside a module type signature - Two ways to define a module to be of a particular module type:
module name : Type =
(*and*)
module name =
- The type checker will ensure that everything defined in signature is defined in the attached module.
- Further it will ensure that only what is specified in signature will be accessible outside of the module. We say that the module is sealed at that signature.
- The definitions in the signature can be very general. (for example for a function giving
int -> int, we can write'a->'aso long as it makes sense) - And vice-versa, we can have
int->int, and we can attach this to a more general function working on anything like'a->'a.