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 (struct
s) and interfaces:
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:
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…
cs2041.org