8.9 Type-level module aliases

(Introduced in OCaml 4.02)

specification::=
module module-name = module-path

The above specification, inside a signature, only matches a moduledefinition equal to module-path. Conversely, a type-level modulealias can be matched by itself, or by any supertype of the type of themodule it references.

There are several restrictions on module-path:

  • it should be of the form M0.M1…Mn (i.e. withoutfunctor applications);
  • inside the body of a functor, M0 should not be one of thefunctor parameters;
  • inside a recursive module definition, M0 should not be one ofthe recursively defined modules.Such specifications are also inferred. Namely, when P is a pathsatisfying the above constraints,
  1. module N = P
  2.  

has type

  1. module N = P

Type-level module aliases are used when checking module pathequalities. That is, in a context where module name N is known to bean alias for P, not only these two module paths check as equal, butF (N) and F (P) are also recognized as equal. In the defaultcompilation mode, this is the only difference with the previousapproach of module aliases having just the same module type as themodule they reference.

When the compiler flag -no-alias-deps is enabled, type-levelmodule aliases are also exploited to avoid introducing dependenciesbetween compilation units. Namely, a module alias referring to amodule inside another compilation unit does not introduce a link-timedependency on that compilation unit, as long as it is notdereferenced; it still introduces a compile-time dependency if theinterface needs to be read, i.e. if the module is a submoduleof the compilation unit, or if some type components are referred to.Additionally, accessing a module alias introduces a link-timedependency on the compilation unit containing the module referenced bythe alias, rather than the compilation unit containing the alias.Note that these differences in link-time behavior may be incompatiblewith the previous behavior, as some compilation units might not beextracted from libraries, and their side-effects ignored.

These weakened dependencies make possible to use module aliases inplace of the -pack mechanism. Suppose that you have a libraryMylib composed of modules A and B. Using -pack, onewould issue the command line

  1. ocamlc -pack a.cmo b.cmo -o mylib.cmo

and as a result obtain a Mylib compilation unit, containingphysically A and B as submodules, and with no dependencies ontheir respective compilation units.Here is a concrete example of a possible alternative approach:

  • Rename the files containing A and B to MylibA andMylibB.
  • Create a packing interface Mylib.ml, containing thefollowing lines.
  1. module A = Mylib__A
  2. module B = Mylib__B
  • Compile Mylib.ml using -no-alias-deps, and the otherfiles using -no-alias-deps and -openMylib (the last one isequivalent to adding the line open!Mylib at the top of eachfile).
  1. ocamlc -c -no-alias-deps Mylib.ml
  2. ocamlc -c -no-alias-deps -open Mylib Mylib__*.mli Mylib__*.ml
  • Finally, create a library containing all the compilation units,and export all the compiled interfaces.
  1. ocamlc -a Mylib*.cmo -o Mylib.cma

This approach lets you access A and B directly inside thelibrary, and as Mylib.A and Mylib.B from outside.It also has the advantage that Mylib is no longer monolithic: ifyou use Mylib.A, only MylibA will be linked in, notMylibB.

Note the use of double underscores in MylibA andMylibB. These were chosen on purpose; the compiler uses thefollowing heuristic when printing paths: given a path LibfooBar,if Lib.FooBar exists and is an alias for LibfooBar, then thecompiler will always display Lib.FooBar instead ofLibfooBar. This way the long Mylib names stay hidden andall the user sees is the nicer dot names. This is how the OCamlstandard library is compiled.