Yao Lirong's Blog

Code Reuse with Modules

2020/02/13

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

1
2
3
4
5
6
7
8
9
10
11
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
end

Encapsulation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
end

The 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 end

N 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
...
end
1
2
module ANewModule = F(OldModule)
module ListSetNoDupsExtended = ExtendSet(ListSetNoDups)

Application

Extension

1
2
3
4
5
6
7
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
end

Other than Extension: Testing

1
2
3
4
5
6
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.

CATALOG
  1. 1. Includes
    1. 1.1. Syntax
    2. 1.2. Encapsulation
    3. 1.3. Includes vs. Open
  2. 2. Functors
    1. 2.1. Syntax
    2. 2.2. Application
      1. 2.2.1. Extension
      2. 2.2.2. Other than Extension: Testing