🧱 Module System Explorer
Master OCaml's powerful module system — from basic modules to functors
What Are Modules?
Modules are OCaml's primary mechanism for organizing code. Every .ml file is implicitly a module. You can also define modules explicitly inside files.
Defining a Module
Using a Module
Nested Modules
🏋️ Exercise: Create a Stack Module
Define a module Stack with type t (an int list), and functions empty, push, pop, and top. pop and top should return option types.
Module Signatures (Interfaces)
A signature (or module type) describes the interface of a module — what types and values it exposes — without revealing implementation details.
.mli files are to .ml files. They define the contract.Constraining a Module
Signature vs No Signature
| Aspect | No Signature | With Signature |
|---|---|---|
| Visibility | Everything exposed | Only declared items visible |
| Type info | Concrete types visible | Can make types abstract |
| Refactoring | Any change breaks users | Internal changes are safe |
| Documentation | Must read implementation | Signature IS the docs |
🏋️ Exercise: Write a Signature
Write a STACK signature with an abstract type t and functions empty : t, push : int -> t -> t, pop : t -> t option, top : t -> int option, and size : t -> int.
Abstract Types & Encapsulation
Abstract types are the key to encapsulation in OCaml. When a signature declares type t without = ..., the internal representation is hidden from users.
with type elt = int sharing constraint reveals just the element type so users can create sets of ints, while keeping the internal representation (t = int list) hidden.Why Abstract Types Matter
🏋️ Exercise: Spot the Leak
This module has an abstraction leak. What is it, and how would you fix it?
Functors — Parameterized Modules
A functor is a function from modules to modules. It takes a module (matching a signature) as input and produces a new module as output. This is OCaml's most powerful abstraction tool.
compare function, and I'll give you back a complete Set implementation."Functor Anatomy
Real-World: Map from Stdlib
🏋️ Exercise: Write a Functor
Write a MakePriorityQueue functor that takes an ORDERED module and provides empty, insert, and pop_min (returns (elt * t) option). Use a sorted list internally.
First-Class Modules
OCaml lets you pack modules into values and unpack them back. This bridges the module system and the core language, enabling powerful dynamic patterns.
Heterogeneous Collections
🏋️ Exercise: Module Selection
Write a function pick_serializer that takes a format string ("json" or "csv") and returns the appropriate first-class module matching a SERIALIZER signature with val serialize : string list -> string.
Module Design Patterns
1. The "Make" Pattern
Standard library convention: a functor named Make that produces a full implementation from a minimal input.
2. The "T" Convention
Name your main type t so it reads naturally with the module name.
3. Include for Extension
Use include to build larger modules from smaller ones.
4. Phantom Types via Modules
Quick Reference
| Pattern | When to Use | Example |
|---|---|---|
module M = struct ... end | Simple namespace | Group related functions |
module type S = sig ... end | Define an interface | Contract for plugins |
M : S | Hide internals | Abstract data types |
module F(X:S) = struct...end | Generic over types | Set.Make, Map.Make |
(module M : S) | Runtime dispatch | Plugin selection |
include M | Extend a module | Add helpers to Base |
S with type t = ... | Reveal one type | Share element type |