Code Reuse with Modules
From Textbook: Code Reuse with Modules
Includes¶
Def: includes enables a structure to include all the values defined by another structure, or a signature to include all the names declared by another signature.
Syntax¶
module type SetExtended = sig
include Set
(*all other definitions specific to SetExtended*)
val of_list : 'a list -> 'a t
end
module ListSetDupsExtended = struct
include ListSetDups
(*all other definitions specific to SetExtended*)
let of_list lst = List.fold_right add lst empty
endEncapsulation¶
module ListSetDupsImpl = struct
type 'a t = 'a list
let empty = []
let mem = List.mem
let add x s = x::s
let elts s = List.sort_uniq Stdlib.compare s
end
module ListSetDups : Set = ListSetDupsImpl
module ListSetDupsExtended = struct
include ListSetDupsImpl
let of_list lst = lst
endThe important change is that ListSetDupsImpl is not sealed, so its type 'a t is not abstract. Plus, OCaml compiler can infer it is an implementation of Set .When we include it in ListSetDupsExtended, we can therefore exploit the fact that it’s a synonym for 'a list.
The clients should use ListSetDups, but when we use List to implement other things, we should use ListSetDupsImpl instead.
Includes vs. Open¶
module M = struct
let x = 0
end
module N = struct
include M
let y = x + 1
let z = 1
end
module O = struct
open M
let y = x + 1
let z = 1
end
module M : sig val x : int end
module N : sig val x : int val y : int val z : int end
module O : sig val y : int val z : int endN has both an x and y, whereas O has only a y. The reason is that include M causes all the definitions of M to also be included in N, so the definition of x from M is present in N. But open M only made those definitions available in the scope of O, aka. a part of the implementation; it doesn’t actually make them part of the structure, aka. the client cannot see them. So O does not contain a definition of x, even though x is in scope during the evaluation of O’s definition of y.
Functors¶
Def: a functor is simply a “function” from structures to structures. It is a parametrized module.
Syntax¶
module F (M : S) = struct
...
end
(*annonymous functors*)
module F = functor (M : S) -> struct
...
end
(*functors parametriezed with multiple modules*)
module F (M1 : S1) ... (Mn : Sn) = struct
...
end
(* above are the desugared version of the codes above*)
module F = functor (M1 : S1) -> ... -> functor (Mn : Sn) -> struct
...
endmodule ANewModule = F(OldModule)
module ListSetNoDupsExtended = ExtendSet(ListSetNoDups)Application¶
Extension¶
module ExtendSet(S:Set) = struct
include S
let add_all lst set =
let add' s x = S.add x s in
List.fold_left add' set lst
endOther than Extension: Testing¶
module SackTester (S: StackSig) = struct
let _ = assert (S.(empty |> push 1 |> peek) = 1)
end
module MyStackTester = StackTester(MyStack)
module ListStackTester = StackTester(ListStack)The only difference is that because the latter example is about extension, we need to include everything from its parent module.