Say I have a module that works with elements which can be compared and have a default value (zero), according to the following module type:
module type Element = sig
type t
val compare : t -> t -> int
val zero : t
end
Now I want to create a functor module Make (Elt : Element) : … = struct … end, which can work with diffent elements and different associated functions compare and zero, and which provides its own functionality: a function named foo serving as an example, which maps a Stdlib.Set of those elements to a single element according to some internal rule (here taking the element that is sorted lowest or the default element if the set is empty).
I wonder how I should define the interface of my functor. A simple approach would be this:
module type S1 = sig
type element
type collection
val foo : collection -> element
end
And then provide a functor like this:
module Make1 (Elt : Element) :
S1 with type element = Elt.t and type collection = Set.Make(Elt).t =
struct
module Collection = Set.Make (Elt)
type element = Elt.t
type collection = Collection.t
let foo (c : collection) : element =
match Collection.min_elt_opt c with
| None -> Elt.zero
| Some elt -> elt
end
But there are more options. Consider the following full code example with S1 through S6 and Make1 through Make6:
module type Element = sig
type t
val compare : t -> t -> int
val zero : t
end
module type S1 = sig
type element
type collection
val foo : collection -> element
end
module type S2 = sig
module Element : Element
val foo : Set.Make(Element).t -> Element.t
end
module type S3 = sig
module Collection : Set.S
val foo : Collection.t -> Collection.elt
end
module type S4 = sig
module Element : Element
type collection = Set.Make(Element).t
val foo : collection -> Element.t
end
module type S5 = sig
type element
module Collection : Set.S with type elt = element
val foo : Collection.t -> element
end
module type S6 = sig
module Element : Element
module Collection : Set.S with type elt = Element.t
val foo : Collection.t -> Element.t
end
module Make1 (Elt : Element) :
S1 with type element = Elt.t and type collection = Set.Make(Elt).t =
struct
module Collection = Set.Make (Elt)
type element = Elt.t
type collection = Collection.t
let foo (c : collection) : element =
match Collection.min_elt_opt c with
| None -> Elt.zero
| Some elt -> elt
end
module Make2 (Elt : Element) : S2 with module Element = Elt = struct
module Element = Elt
module Collection = Set.Make (Element)
let foo (c : Collection.t) : Elt.t =
match Collection.min_elt_opt c with
| None -> Elt.zero
| Some elt -> elt
end
module Make3 (Elt : Element) :
S3 with module Collection = Set.Make(Elt) = struct
module Collection = Set.Make (Elt)
let foo (c : Collection.t) : Collection.elt =
match Collection.min_elt_opt c with
| None -> Elt.zero
| Some elt -> elt
end
module Make4 (Elt : Element) : S4 with module Element = Elt = struct
module Element = Elt
module Collection = Set.Make (Element)
type collection = Collection.t
let foo (c : collection) : Elt.t =
match Collection.min_elt_opt c with
| None -> Elt.zero
| Some elt -> elt
end
module Make5 (Elt : Element) :
S5 with type element = Elt.t and module Collection = Set.Make(Elt) =
struct
type element = Elt.t
module Collection = Set.Make (Elt)
let foo (c : Collection.t) : element =
match Collection.min_elt_opt c with
| None -> Elt.zero
| Some elt -> elt
end
module Make6 (Elt : Element) :
S6 with module Element = Elt and module Collection = Set.Make(Elt) =
struct
module Element = Elt
module Collection = Set.Make (Elt)
let foo (c : Collection.t) : Elt.t =
match Collection.min_elt_opt c with
| None -> Elt.zero
| Some elt -> elt
end
Is there a “best” way to go? Or does this depend on further questions, and if yes, which ones? What is idiomatic here?
Your advice is appreciated. ![]()