Signatures

Module types let us describe groups of related modules. The syntax for defining a module type is:

module type ModuleTypeName = sig 
  (* declarations *)
end

Here is a module type for stacks:

module type Stack = sig
  type 'a stack
  val empty    : 'a stack
  val is_empty : 'a stack -> bool
  val push     : 'a -> 'a stack -> 'a stack
  val peek     : 'a stack -> 'a
  val pop      : 'a stack -> 'a stack
end

By convention, the module type name is capitalized, but it does not have to be. There is an older convention from the SML language that signature names are in ALLCAPS, and you might occasionally see that still, but we don't typically follow it in OCaml.

The part of the module type that is written

sig (* declarations *) end

is called a signature. A signature is simply a sequence of declarations. The signature itself is anonymous—it has no name—until it is bound to a name by a module type definition. The syntax val id : t means that there is a value named id whose type is t.

A structure matches a signature if the structure provides definitions for all the names specified in the signature (and possibly more), and these definitions meet the type requirements given in the signature. Usually, a definition meets a type requirement by providing a value of exactly that type. But the definition could instead provide a value that has a more general type. For example:

module type Sig = sig
  val f : int -> int
end

module M1 : Sig = struct
  let f x = x+1
end

module M2 : Sig = struct
  let f x = x
end

Module M1 provides a function f of exactly the type specified by Sig, namely, int->int. Module M2 provides a function that is instead of type 'a -> 'a. Both M1 and M2 match Sig. Note that anywhere a value v1 of type int->int is needed, it's safe to instead use a value v2 of type 'a -> 'a. That's because if we apply v2 to an int, its type guarantees us that we will get an int back.

Returning to our example, the structure given above for ListStack doesn't yet match the signature given above for Stack, because that structure doesn't define the type 'a stack. So we could amend the definition of ListStack to:

module ListStack = struct
  type 'a stack = 'a list
  (* the rest is the same as before *)
end

Now that structure matches the signature of Stack. We can ask the compiler to check that by providing a module type annotation for the module:

module ListStack : Stack = struct
  type 'a stack = 'a list
  (* the rest is the same as before *)
end

The type 'a stack is an example of a representation type: a type that is used to represent a version of a data structure. Here, we're implementing stacks using lists, so the representation type is a list.

results matching ""

    No results matching ""