Encapsulation and Includes
We mentioned above that you might wonder why we didn't write this simpler definition
of of_list:
let of_list lst = lst
The reason is that includes must obey encapsulation, just like the rest
of the module system. ListSetDups was sealed with the module type
Set, thus making 'a t abstract. So even ListSetDupsExtended is
forbidden from knowing that 'a t and 'a list are synonyms.
A standard way to solve this problem is to rewrite the definitions as folllows:
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. When we include it in ListSetDupsExtended, we can therefore
exploit the fact that it's a synonym for 'a list.
What we just did is effectively the same as what Java does to handle the
visibility modifiers public, private, etc. The "private version" of
a class is like the Impl version above: anyone who can see that
version gets to see all the exposed "things" (fields in Java, types in
OCaml), without any encapsulation. The "public version" of a class is
like the sealed version above: anyone who can see that version is
forced to treat the "things" (fields in Java, types in OCaml) as abstract,
hence encapsulated.