CSCI 2041

ADVANCED PROGRAMMING PRINCIPLES

Module Signatures

Previously:

Modules collect code and types into units: module.ml creates the ‘Module’ namespace.

Interfaces can be used to hide implementation details and restrict access to the contents of a module: the module.mli file controls what other modules can access.

struct

OCaml also allows nested modules (structs) and interfaces:

module MyMod = struct
(* declare some stuff here like *)
let life_the_universe_and_everything = 42
(* could put more stuff here too *)
end;;
# MyMod.life_the_universe_and_everything;;
module Queue = struct
  type 'a q = 'a list
  let empty_q = []
  let empty q = match q with [] -> true
  | _ -> false
  exception EmptyQ
  let peek q = match q with [] -> raise EmptyQ
  | h::tq -> h
  let enqueue q x = q @ [x]
  let dequeue q = match q with [] -> raise EmptyQ
  | h::tq -> tq
end

How do we make Queue.q abstract?

sig

A signature is the equivalent of an interface file:

module type QSig = sig
  type 'a q
  val empty_q : 'a q
  val empty : 'a q -> bool
  exception EmptyQ
  val peek : 'a q -> 'a
  val enqueue : 'a q -> 'a -> 'a q
  val dequeue : 'a q -> 'a q
end

We can declare a new name for a module restricted to a signature:

module AbsQ = (Queue : QSig)

Or specify the signature in the module declaration:

module Queue : QSig = struct ...

What if we want to expose the implementation type?

module LeakyQ : QSig
    with type 'a q = 'a option =
struct
 type 'a q = 'a option
 let empty_q = None
 let empty q = (q=None)
 let enqueue q x = match q with
  | None -> Some x | _ -> q
 exception EmptyQ
 let peek q = match q with
  | None -> raise EmptyQ | Some x -> x
 let dequeue q = match q with
  | None -> raise EmptyQ | Some x -> None
end

Such type constraints (with type τ = σ) make abstract types concrete.

module FastQ : QSig = struct
 type 'a q = 'a list * 'a list
 let empty_q = ([],[])
 let is_empty q = match q with
  | ([],_) -> true | _ -> false
 let enqueue q x = match q with
  | ([],[]) -> ([x],[])
  | (f,b) -> (f,x::b)
 exception EmptyQ
 let peek q = match q with
  | ([],_) -> raise EmptyQ
  | (h::t,_) -> h
 let dequeue q = match q with
  | ([],_) -> raise EmptyQ
  | ([x],back) -> (List.rev back, [])
  | (h::f,b) -> (f,b)
end

Testing out the code…

let build_sloq n =
  let rec qbuild i q = if i=0 then q
    else qbuild (i-1) (Queue.enqueue q i)
  in qbuild n Queue.empty_q
let empty_sloq q = if Queue.is_empty q then ()
  else let q' = Queue.dequeue q in
  empty_sloq q'
let build_fq n =
  let rec qbuild i q = if i=0 then q
  else qbuild (i-1) (FastQ.enqueue q i)
  in qbuild FastQ.empty_q
let empty_fq q = if FastQ.is_empty q then ()
  else let q' = FastQ.dequeue q in
  empty_fq q'

cs2041.org

// reveal.js plugins