I think first class modules might be what I want, but I’m not sure. Here’s what I’m trying to do:
Let’s say I have some items which I want to add to a container but at runtime I want to decide whether items in the container should be unique. If true I want to use a Set if false I want to use a List.
In other languages this would be simple to do at runtime by creating different instances of an interface, what is the idiomatic way of doing this kind of think in OCaml?
Here’s a silly example of the kind of thing I’d like to be able to do:
let count_items (unique : bool) (items : int list) =
let container =
if unique then Thing.empty (module Set) else Thing.empty (module List)
in
let added = List.fold items ~init:container ~f:(fun c v -> Thing.add c v) in
Thing.count added
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