summaryrefslogtreecommitdiff
path: root/manual/src/refman/extensions/modulealias.etex
blob: 49e3522885e1fc5705dc64784213703867683e6f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
(Introduced in OCaml 4.02)

\begin{syntax}
specification:
          ...
        | 'module' module-name '=' module-path
\end{syntax}

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

There are several restrictions on @module-path@:
\begin{enumerate}
\item it should be of the form \(M_0.M_1...M_n\) ({\em i.e.} without
  functor applications);
\item inside the body of a  functor, \(M_0\) should not be one of the
  functor parameters;
\item inside a recursive module definition, \(M_0\) should not be one of
  the recursively defined modules.
\end{enumerate}

Such specifications are also inferred. Namely, when @P@ is a path
satisfying the above constraints,
\begin{caml_eval}
module P = struct end
\end{caml_eval}
\begin{caml_example*}{verbatim}
module N = P
\end{caml_example*}
has type
\begin{caml_example*}{signature}
module N = P
\end{caml_example*}

Type-level module aliases are used when checking module path
equalities. That is, in a context where module name @N@ is known to be
an alias for @P@, not only these two module paths check as equal, but
@F(N)@ and @F(P)@ are also recognized as equal. In the default
compilation mode, this is the only difference with the previous
approach of module aliases having just the same module type as the
module they reference.

When the compiler flag @'-no-alias-deps'@ is enabled, type-level
module aliases are also exploited to avoid introducing dependencies
between compilation units. Namely, a module alias referring to a
module inside another compilation unit does not introduce a link-time
dependency on that compilation unit, as long as it is not
dereferenced; it still introduces a compile-time dependency if the
interface needs to be read, {\em i.e.}  if the module is a submodule
of the compilation unit, or if some type components are referred to.
Additionally, accessing a module alias introduces a link-time
dependency on the compilation unit containing the module referenced by
the alias, rather than the compilation unit containing the alias.
Note that these differences in link-time behavior may be incompatible
with the previous behavior, as some compilation units might not be
extracted from libraries, and their side-effects ignored.

These weakened dependencies make possible to use module aliases in
place of the @'-pack'@ mechanism. Suppose that you have a library
@'Mylib'@ composed of modules @'A'@ and @'B'@. Using @'-pack'@, one
would issue the command line
\begin{verbatim}
ocamlc -pack a.cmo b.cmo -o mylib.cmo
\end{verbatim}
and as a result obtain a @'Mylib'@ compilation unit, containing
physically @'A'@ and @'B'@ as submodules, and with no dependencies on
their respective compilation units.
Here is a concrete example of a possible alternative approach:
\begin{enumerate}
\item Rename the files containing @'A'@ and @'B'@ to @'Mylib__A'@ and
  @'Mylib__B'@.
\item Create a packing interface @'Mylib.ml'@, containing the
  following lines.
\begin{verbatim}
module A = Mylib__A
module B = Mylib__B
\end{verbatim}
\item Compile @'Mylib.ml'@ using @'-no-alias-deps'@, and the other
  files using @'-no-alias-deps'@ and @'-open' 'Mylib'@ (the last one is
  equivalent to adding the line @'open!' 'Mylib'@ at the top of each
  file).
\begin{verbatim}
ocamlc -c -no-alias-deps Mylib.ml
ocamlc -c -no-alias-deps -open Mylib Mylib__*.mli Mylib__*.ml
\end{verbatim}
\item Finally, create a library containing all the compilation units,
  and export all the compiled interfaces.
\begin{verbatim}
ocamlc -a Mylib*.cmo -o Mylib.cma
\end{verbatim}
\end{enumerate}
This approach lets you access @'A'@ and @'B'@ directly inside the
library, and as @'Mylib.A'@ and @'Mylib.B'@ from outside.
It also has the advantage that @'Mylib'@ is no longer monolithic: if
you use @'Mylib.A'@, only @'Mylib__A'@ will be linked in, not
@'Mylib__B'@.
%Note that in the above @'Mylib.cmo'@ is actually empty, and one could
%name the interface @'Mylib.mli'@, but this would require that all
%clients are compiled with the @'-no-alias-deps'@ flag.

Note the use of double underscores in @'Mylib__A'@ and
@'Mylib__B'@. These were chosen on purpose; the compiler uses the
following heuristic when printing paths: given a path @'Lib__fooBar'@,
if @'Lib.FooBar'@ exists and is an alias for @'Lib__fooBar'@, then the
compiler will always display @'Lib.FooBar'@ instead of
@'Lib__fooBar'@. This way the long @'Mylib__'@ names stay hidden and
all the user sees is the nicer dot names. This is how the OCaml
standard library is compiled.