Author: Christian.Queinnec@lip6.fr

## Modules as Compilation Units

The Objective CAML distribution includes a number of predefined modules. We saw in chapter 8 how to use these modules in a program. Here, we will show how users can define similar modules.

### Interface and Implementation

The module `Stack` from the distribution provides the main functions on stacks, that is, queues with ``last in, first out'' discipline.

    # let queue = Stack.create () ;;
    val queue : '_a Stack.t = <abstr>
    # Stack.push 1 queue ; Stack.push 2 queue ; Stack.push 3 queue ;;
    - : unit = ()
    # Stack.iter (fun n -> Printf.printf "%d " n)  queue ;;
    3 2 1 - : unit = ()

Since Objective CAML is distributed with full source code, we can look at the actual implementation of stacks.
`ocaml-2.04/stdlib/stack.ml``
`type`` 'a`` t`` `=``` {`` mutable`` c`` `:``` 'a`` list`` }
exception`` Empty
let`` create`` ()`` `=``` {`` c`` `=``` []`` }
let`` clear`` s`` `=``` s`.`c`` `<-``` []
let`` push`` x`` s`` `=``` s`.`c`` `<-``` x`` ::`` s`.`c
let`` pop`` s`` `=``` match`` s`.`c`` with`` `` hd::tl`` ->`` s`.`c`` `<-``` tl;`` hd`` `` `|``` `` []`` `` ->`` raise`` Empty
let`` length`` s`` `=``` List.length`` s`.`c
let`` iter`` f`` s`` `=``` List.iter`` f`` s`.`c
We see that the type of stacks (written `Stack.t` outside the `Stack` module and just `t` inside) is a record with one mutable field containing a list. The list holds the contents of the stack, with the list head corresponding to the stack top. Stack operations are implemented as the basic list operations applied to the field of the record.
Armed with this insider's knowledge, we could try to access directly the list representing a stack. However, Objective CAML will not let us do this.

    # let list = queue.c ;;
    Characters 12-19:
    Unbound label c

The compiler complains as if it did not know that `Stack.t` is a record type with a field `c`. It is actually the case, as we can see by looking at the interface of the `Stack` module.
`ocaml-2.04/stdlib/stack.mli``
``(* Module [Stack]: last-in first-out stacks *)`
`(* This module implements stacks (LIFOs), with in-place modification. *)`
type`` 'a`` t`` `` `` `` `` `` `` `` `` `(* The type of stacks containing elements of type ['a]. *)`
exception`` Empty`` `` `` `(* Raised when [pop] is applied to an empty stack. *)`
val`` create`:``` unit`` ->`` 'a`` t
`` `` `` `` `` `` `` `` `(* Return a new stack, initially empty. *)`
val`` push`:``` 'a`` ->`` 'a`` t`` ->`` unit
`` `` `` `` `` `` `` `` `(* [push x s] adds the element [x] at the top of stack [s]. *)`
val`` pop`:``` 'a`` t`` ->`` 'a
`` `` `` `` `` `` `` `` `(* [pop s] removes and returns the topmost element in stack [s],`
`or raises [Empty] if the stack is empty. *)`
val`` clear`` `:``` 'a`` t`` ->`` unit
`` `` `` `` `` `` `` `` `(* Discard all elements from a stack. *)`
val`` length`:``` 'a`` t`` ->`` int
`` `` `` `` `` `` `` `` `(* Return the number of elements in a stack. *)`
val`` iter`:``` `(`'a`` ->`` unit`)``` ->`` 'a`` t`` ->`` unit
`` `` `` `` `` `` `` `` `(* [iter f s] applies [f] in turn to all elements of [s],`
`from the element at the top of the stack to the element at the`
`bottom of the stack. The stack itself is unchanged. *)`
In addition to comments documenting the functions of the module, this file lists explicitly the value, type and exception identifiers defined in the file `stack.ml` that should be visible to clients of the `Stack` module. More precisely, the interface declares the names and type specifications for these exported definitions. In particular, the type name `t` is exported, but the representation of this type (that is, as a record with one `c` field) is not given in this interface. Thus, clients of the `Stack` module do not know how the type `Stack.t` is represented, and cannot access directly values of this type. We say that the type Stack.t is abstract, or opaque.
The interface also declares the functions operating on stacks, giving their names and types. (The types must be provided explicitly so that the type checker can check that these functions are correctly used.) Declaration of values and functions in an interface is achieved via the following construct:

### Syntax

val nom : type

### Relating Interfaces and Implementations

As shown above, the `Stack` is composed of two parts: an implementation providing definitions, and an interface providing declarations for those definitions that are exported. All module components declared in the interface must have a matching definition in the implementation. Also, the types of values and functions as defined in the implementation must match the types declared in the interface.
The relationship between interface and implementation is not symmetrical. The implementation can contain more definitions than requested by the interface. Typically, the definition of an exported function can use auxiliary functions whose names will not appear in the interface. Such auxiliary functions cannot be called directly by a client of the module. Similarly, the interface can restrict the type of a definition. Consider a module defining the function `id` as the identity function (let`` id`` x`` `=``` x). Its interface can declare `id` with the type int -> nt (instead of the more general 'a -> a). Then, clients of this module can only apply `id` to integers.
Since the interface of a module is clearly separated from its implementation, it becomes possible to have several implementations for the same interface, for instance to test different algorithms or data structures for the same operations. As an example, here is an alternate implementation for the `Stack` module, based on arrays instead of lists.

    type 'a t = { mutable sp : int; mutable c : 'a array }
    exception Empty
    let create () = { sp=0 ; c = [||] }
    let clear s = s.sp <- 0; s.c <- [||]
    let size = 5
    let increase s = s.c <- Array.append s.c (Array.create size s.c.(0))

    let push x s =
      if s.sp >= Array.length s.c then increase s ;
      s.c.(s.sp) <- x ;
      s.sp <- succ s.sp

    let pop s =
      if s.sp = 0 then raise Empty
      else let x = s.c.(s.sp) in s.sp <- pred s.sp ; x

    let length s = s.sp
    let iter f s = for i = pred s.sp downto 0 do f s.sc.(i) done

This new implementation satisfies the requisites of the interface file `stack.mli`. Thus, it can be used instead of the predefined implementation of `Stack` in any program.

### Separate Compilation

Like most modern programming languages, Objective CAML supports the decomposition of programs into multiple compilation units, separately compiled. A compilation unit is composed of two files, an implementation file (with extension `.ml`) and an interface file (with extension `.mli`). Each compilation unit is viewed as a module. Compiling the implementation file `name.ml` defines the module named `Name`1.
Values, types and exceptions defined in a module can be referenced either via the dot notation (`Module`.`identifier`), also known as qualified identifiers, or via the open construct.

`a.ml` | `b.ml`
type`` t`` `=``` {`` x`:`int`` ;`` y`:`int`` }`` ;; | let`` val`` `=``` {`` A.x`` `=``` `1``` ;`` A.y`` `=``` `2``` }`` ;;
let`` f`` c`` `=``` c`.`x`` `+``` c`.`y`` ;; | A.f`` val`` ;;
| open`` A`` ;;
| f`` val`` ;;

An interface file (`.mli` file) must be compiled using the `ocamlc -c` command before any module that depends on this interface is compiled; this includes both clients of the module and the implementation file for this module as well.
If no interface file is provided for an implementation file, Objective CAML considers that the module exports everything; that is, all identifiers defined in the implementation file are present in the implicit interface with their most general types.
The linking phase to produce an executable file is performed as described in chapter 7: the `ocamlc` command (without the `-c` option), followed by the object files for all compilation units comprising the program. Warning: object files must be provided on the command line in dependency order. That is, if a module `B` references another module `A`, the object file `a.cmo` must precede `b.cmo` on the linker command line. Consequently, cross dependencies between two modules are forbidden.
For instance, to generate an executable file from the source files `a.ml` and `b.ml`, with matching interface files `a.mli` and `b.mli`, we issue the following commands:

    > ocamlc -c a.mli
    > ocamlc -c a.ml
    > ocamlc -c b.mli
    > ocamlc -c b.ml
    > ocamlc a.cmo b.cmo

Compilation units, composed of one interface file and one implementation file, support separate compilation and information hiding. However, their abilities as a general program structuring tool are low. In particular, there is a one-to-one connection between modules and files, preventing a program to use simultaneously several implementations of a given interface, or also several interfaces for the same implementation. Nested modules and module parameterization are not supported either. To palliate those weaknesses, Objective CAML offers a module language, with special syntax and linguistic constructs, to manipulate modules inside the language itself. The remainder of this chapter introduces this module language.