Modules & Signatures

math_utils.ml automatically defines module Math_utils:

20.1 Every file is a module

math_utils.ml automatically defines module Math_utils:

(* math_utils.ml *)
let square x = x * x
let cube x = x * x * x
(* main.ml *)
let () = print_int (Math_utils.square 5)

20.2 Explicit modules (structures)

module Counter = struct
  type t = int
  let zero = 0
  let incr c = c + 1
  let to_string = string_of_int
end

let c = Counter.(incr (incr zero))   (* local open *)

20.3 Signatures (module types)

Outside the module, 'a ListStack.t is abstract — clients cannot see that it is a list. This is information hiding / encapsulation.

module type STACK = sig
  type 'a t                       (* abstract type *)
  exception Empty
  val empty : 'a t
  val is_empty : 'a t -> bool
  val push : 'a -> 'a t -> 'a t
  val pop : 'a t -> 'a * 'a t     (* raises Empty *)
  val size : 'a t -> int
end

module ListStack : STACK = struct
  type 'a t = 'a list
  exception Empty
  let empty = []
  let is_empty s = s = []
  let push x s = x :: s
  let pop = function [] -> raise Empty | x :: s -> (x, s)
  let size = List.length
end

20.4 Interface files .mli

stack.ml contains the implementation. Anything not in the .mli is private. Always write .mli files for libraries.

(** A functional stack. *)
type 'a t
val empty : 'a t
val push : 'a -> 'a t -> 'a t
val pop : 'a t -> ('a * 'a t) option

20.5 Opening modules

open List                    (* global open: brings everything in scope (use sparingly) *)
let open List in map f xs    (* local open *)
List.(map f xs)              (* local open, compact *)
open! Base                   (* open, silencing shadowing warning *)
module L = List              (* alias *)

20.6 Nested modules

module Geometry = struct
  module Point = struct
    type t = { x : float; y : float }
    let origin = { x = 0.; y = 0. }
  end
  module Circle = struct
    type t = { center : Point.t; radius : float }
  end
end
let p = Geometry.Point.origin

20.7 include

module ExtList = struct
  include List
  let sum = fold_left ( + ) 0
end

module type EXT_STACK = sig
  include STACK
  val peek : 'a t -> 'a option
end

20.8 Abstract types & smart constructors

Now every Email.t in the program is guaranteed to have been validated.

module Email : sig
  type t
  val of_string : string -> t option
  val to_string : t -> string
end = struct
  type t = string
  let of_string s = if String.contains s '@' then Some s else None
  let to_string s = s
end

20.9 Private types

module Positive : sig
  type t = private int        (* readable as int, but only constructible via make *)
  val make : int -> t option
end = struct
  type t = int
  let make n = if n > 0 then Some n else None
end

let f (p : Positive.t) = (p :> int) + 1    (* coercion to int allowed *)