OCaml Modules

kuniga.me > NP-Incompleteness > OCaml Modules

# OCaml Modules

18 Jul 2016

One prevalent syntax in some OCaml code I’ve encountered is about modules, so I decided to study them a little bit to be able to better understand the code I’ve been reading.

This post is based on Chapters 4 and 9 from Real World OCaml [1, 2], which we started in the last post.

### Defining modules

By default, a file defines a module. Module names are derived from filenames and is always capitalized (even if the filename is not). Let’s define a toy example:

myModule.ml:

main.ml:

We can now compile using the built-in ocamlc (make sure to follow the setup here):

Note that the order is important. Since main.ml depends on myModule.ml, it has to be included first. In case the files do not live in the same directory, we can use the -I option. For example, if myModule was in another_dir, we could compile using:

A module has two parts: the definition and the signature. A module defined by a file filename.ml can be constrained by a signature in a file called filename.mli. The definition (mli) should be included in the compiling step and appear before the definition. For example, we could have

myModule.mli:

and compile using:

Submodules

It’s possible to define multiple modules inside a file through submodules. We can add this to myModule.ml:

and in main.ml:

Note that we still need to provide the module name defined by the filename. The first part of the definition is the type signature of the module and the second the definition. The general syntax is:

Alternatively, we can separate the type definition from the implementation. In that case, we create a separate file with extension .mli containing the interface

myModule.mli:

and in myModule.ml:

In general, the syntax for the signature is:

and for the module definition is:

### Including modules

Modules are made available when linking during compile time, but if we want to use a function from a module, we still need to qualify it. We can alternatively include them explicitly to avoid qualifying module names (similar to use namespace in C++).

We can also invoke open inline:

or alias the module to a shorter name with the let module construct:

### Functors

Functors are functions that transform modules, so it’s a function from a module to a module. A basic example is provided in [2]. First we define a toy signature signature:

Then, we define a functor, which we call Increment:

What tells us this is a function is the extra parameter the module takes (M: X_int). In here, M is the name we give to the module and X_int is its interface. Since the interface tells us M has the x value, we can access it within our function. Note that Increment acts like a function, taking M (module) as parameter and returning another module, defined by the struct block. In this case, the type signature is different because the returned module has y instead of x. We can force the returned type of a function by adding a constraint:

Now, if we try to use y, we can a compilation error. To fix it, we just change y to x.

Functors cannot be used by themselves. They’re useful for creating modules out of existing modules. For an example, imagine we have a module implementing X_int:

We can create a new module Four, by transforming our Three module:

### “Abstract” functors

In [2], the authors provide an example of an MakeInterval module, in which there’s a dependent type. First it creates a Comparable signature:

to make it shorter (and less “real world”), I’ve created a simpler version here, MakeElement:

we can then create a module:

The above works because Int satisfies the constraint defined by the Comparable module signature. The authors make a point here that sticking to standard conventions can improve reuse such as cases like this. We can finally use

The authors argue that the IntElement exposes implementation details because Element is “exposed”:

One solution is to constrain the return type of the functor and not expose Element in the signature. The signature would look like:

and the functor would look like:

The problem is the type element is not bound to anything, so we have to explicitly do it when defining the functor. The construct is the following

now MakeElement return a module with interface ElementInterface which doesn’t expose Element.

### Destructive Substitution

One problem with the approach above is the redundant binding of type element. One slightly different syntax removes that requirement:

We basically changed from = to :=, which is called destructive substitution.

### Multiple Interfaces

It’s possible to “extend” more than one module signature when creating a new one. For example:

### Conclusion

Modules seems a very powerful concept in Ocaml. Besides organizing files, modules can act as functions and can model concepts from Object Oriented Programming like classes and interfaces.

I’ve been following a different study strategy while learning Ocaml. I try to read some real world code and when I get stuck understand a syntax or a pattern, I can search for them in a book. This makes it more interesting than reading a book cover to cover.

### References

• [1] Real World OCaml – Chapter 4. Files, Modules, and Programs
• [2] Real World OCaml – Chapter 9. Functors
• [3] Compiling OCaml projects
Tags: ocaml