Wiki
Clone wikihelium / popl20 / Tutorial_Modules
Tutorial 4: Module system
Structures
In Helium we have sophisticated module system similar to one that can be found in OCaml or SML. The simpliest way to define a module is to define a structure that groups several definitions.
module B = struct data MyBool = MyTrue | MyFalse let ofBool b = if b then MyTrue else MyFalse let toBool m = match m with | MyTrue => True | MyFalse => False end end
B
by providing a qualified name:
[IO]> B.toBool B.MyTrue ;; False
let negB m = open B in match m with | MyTrue => MyFalse | MyFalse => MyTrue end
List
(e.g. map
or length
) are also part of module A
,
but they do not appear in module B
.
module A = struct include List let length2 xs ys = length xs + length ys end module B = struct open List let length2 xs ys = length xs + length ys end
Signatures and abstract types
The key feature of a module system is hiding of implementation details. It can be obtained by providing more specific signatures (types of modules) to a modules.
module AbstractB : sig type MyBool val ofBool : Bool -> MyBool val toBool : MyBool -> Bool end = B
AbstractB
contains type MyBool
, but its
implementation is not revealed. Such a type is called abstract.
Refering to AbstractB.MyTrue
would end up
with a type error. We could exchange implementation of AbstractB
to
a different one, without affecting the rest of the program, e.g.,
module AbstractB : sig type MyBool val ofBool : Bool -> MyBool val toBool : MyBool -> Bool end = struct type MyBool = Bool let ofBool b = b let toBool b = b end
Source files as modules
Each source file of Helium with extension .he
is in fact a body of structure
definition. When you mention a name of module (e.g. Modl
) in your source
code and such a module is not locally defined nor visible in opened modules,
then the interpreter would try to find source file with corresponding name
(Modl.he
) in the following locations (order matters):
- in the same directory,
- in directories passed to interpreter using
-I
option, - in library directories, i.e., colon-separated list of directories stored
in
HELIUM_LIB
environmental variable orlib/
when this variable is not defined.
Additionally, when the file is associated with file with .she
file
(e.g. Modl.she
), the contents of .she
file is treated as a body of
signature for a module.
Modules as types
In larger projects, programmers often dedicate separate file for each
datatype. With this aproach, types have two names: name of the module
and name of a type inside a module. In Helium, we can share these two
names, and refer to types by only the name of the module.
When a module M
contains type or effect named with keyword this
,
then this module can be used in a place where a type is expected.
module B = struct data this = MyTrue | MyFalse let ofBool b = if b then MyTrue else MyFalse let toBool (m : this) = match m with | MyTrue => True | MyFalse => False end end let idB (b : B) : B = B.ofBool (B.toBool b)
Types as modules
Each datatype or effect is also visible for a module system as a separate module that contains type definition and constructors / fields / operations. This feature allows easly to avoid confusion, while defining several types with the same names of constructors in one module.
data TypeA = A | B data TypeB = B | C let pairOfB = (TypeA.B, TypeB.B)
TODO: include type
Functors
Updated