The most direct translation of what you propose is to have a common signature for both modules:
module type S = sig
type elt
type t
val empty : t
val add: elt -> t -> t
val count: t -> int
end
and two ways to produce this signature, from a list and from a set:
module S_list (T : sig type t end): S = struct
type elt = T.t
type t = elt list
let empty = []
let add x l = x :: l
let count = List.length
end
module S_set (O : Set.OrderedType) : S = struct
module Set = Set.Make (O)
type elt = O.t
type t = Set.t
let empty = Set.empty
let add = Set.add
let count = Set.cardinal
end
Then you can just use switch between the two implementations depending on the value of unique
:
module S_intlist = S_list (struct type t = int end)
module S_intset = S_set (struct type t = int let compare = compare end)
let count_items (unique : bool) (items : int list) =
let (module Thing) = if unique then (module S_intset : S) else (module S_intlist : S) in
let container = Thing.empty in
let added = List.fold items ~init:container ~f:(fun c v -> Thing.add c v) in
Thing.count added
Cheers,
Nicolás