OCaml namespacing Last updated Feb 11, 2025
Overview
The OCaml language is fully namespaced. Every module determines a namespace, and all code must be written within a module.
However, the mapping from OCaml namespaces (modules) to the filesystem
is more restricted. Unlike many languages (Java, Rust, Go, etc.), the
OCaml language does not define a mapping from module paths like
A.B.f
to a directory hierarchy in the file system. Instead, the
OCaml compiler defines a mapping from single modules to the file
system. When it tries to compile A.B.f
, it does not look for f
in
a/b.ml
; instead it looks for a definition of A
in the current
file; if it does not find one, it searches the file system (in the
directories set using the -I
compiler option) for a compiled module
named A
. Specifically, it looks for a.cmi
(or equivalently
A.cmi
). If it finds what it’s looking for, it then proceeds to look
for a definition of B
, in similar fashion, and proceeds in the same
way until it finds a definition of f
.
In other words, the search space for the compiler (and linker) is
flat. The drawback of this flat-namespace implementation is that a
project may contain multiple modules with the same name, for example
foo/a.ml
and bar/a.ml
. Then you may end up with both A
modules
in the search path, with no reliable way to control which one the
compiler finds. You can avoid this in your own code by using unique
names, but you cannot do much to eliminate the possibility that
external libraries may introduce name clashes.
One way around this problem is to use pseudo-namespacing in your file
names. Instead of foo/a.ml
and foo/b.ml
, you use foo__a.ml
and foo_b.ml
or similar. This does not eliminate the
possibility of name clashes (a library you use might have the same
names) but it reduces it considerably. The major drawback of this
method is that you must use the same name in your code, writing
Foo__a.Foo__b.f
instead of A.B.f
.
Type-level module aliases
Version 4.02 of the compiler introduced Type-level module aliases, which can be used to emulate filesystem-based namespacing.
Implementation techniques
Module aliasing is a very flexible technique that can be used in ways that are not possible in languages with predefined language/filesystem mapping.
Open accessibility
In most languages with predefined filesystem namespacing, an object like
a/b/c
can only be accessed via its fully qualified name. But in
OCaml, it is possible to access the components of a namespace
directly, without namespace qualification. That’s because segmented
names like A.B.C
always resolve to a single compiled module, e.g.
a__B__C
or b__C
. So if you have a namespace with a large
number of components, you always have the option of depending directly
on just one or a few of its components, rather than relying on the
entire namespace (i.e. as a library).
Composability
OCaml pseudo-namespaces are composable. Suppose module C
is in
namespace B
, so it can be accessed as B.C
(which might resolve to
b__C
for example). Then B
can be included in namespace A
, so
that C
can also be accessed as A.B.C
. For example, a.ml
might
contain aliasing equation module B = B
, and since B.ml
already
contains aliasing equation module C = B__C
, both B.C
and A.B.C
will resolve to b__C
.
Private namespaces
Example: ocaml-re, a regular expression library for OCaml.