How to apply [@@deriving show] to a type from module parameter of my functor?

I have a functor that takes a Set type like:

module type MySet = functor (S : Set.S) -> sig
  val my_method : S.t -> S.elt -> S.elt list option
end

module MySet_Make : MySet = functor (S : Set.S) -> struct
  let my_method set el = Some [el]  (* whatever *)
end

module IntSet = Set.Make(Int)
module MyIntSet = MySet_Make(IntSet)

S.elt is the type of elements of the set

I want to apply [@@deriving show] (from https://github.com/ocaml-ppx/ppx_deriving#plugin-show) to S.elt within my functor somehow, so that in one of my methods I can rely on having a show : S.elt -> string function available.

I feel like it must be possible but I can't work out the right syntax.

Alternatively - if there's a way to specify in the signature that the Set type S was made having elements of a "showable" type.

e.g. I can define:

module type Showable = sig
  type t [@@deriving show]
end

...but I can't work out how to specify that as a type constraint to elements of (S : Set.S)


Solution 1:

You can construct new signatures that specify the exact function show you need:

module MySet_Make(S : sig
  include Set.S
  val show : elt -> string
end) = struct
  let my_method _set el =
    print_endline (S.show el);
    Some [el]
end

Then you can build the actual module instance by constructing the module with the needed function:

module IntSet = struct
  include Set.Make(Int)

  (* For other types, this function could be created by just using [@@deriving show] *)
  let show = string_of_int
end
module MyIntSet = MySet_Make(IntSet)

Solution 2:

Ok, after a couple of hours more fumbling around in the dark I found a recipe that does everything I wanted...

First we define a "showable" type, representing a module type that has had [@@deriving show] (from https://github.com/ocaml-ppx/ppx_deriving#plugin-show) applied to it:

module type Showable = sig
  type t
  val pp : Format.formatter -> t -> unit
  val show : t -> string
end

(I don't know if there's some way to get this directly from ppx_deriving.show without defining it manually?)

Then we re-define and extend the Set and Set.OrderedType (i.e. element) types to require that the elements are "showable":

module type OrderedShowable = sig
  include Set.OrderedType
  include Showable with type t := t
end

module ShowableSet = struct
  include Set
  module type S = sig
    include Set.S
  end
  module Make (Ord : OrderedShowable) = struct
    include Set.Make(Ord)
  end
end

I think with the original code in my question I had got confused and used some kind of higher-order functor syntax (?) ...I don't know how it seemed to work at all, but at some point I realised my MySet_Make was returning a functor rather than a module. So we'll fix that now and just use a normal functor.

The other thing we can fix is to make MySet a further extension of ShowableSet ... so MySet_Make will take the element type as a parameter instead of another Set type. This makes the eventual code all simpler too:

module type MySet = sig
  include ShowableSet.S
  val my_method : t -> elt -> elt list option
  val show_el : elt -> string
end

module AdjacencySet_Make (El : OrderedShowable) : AdjacencySet
  with type elt = El.t
= struct
  include ShowableSet.Make(El)
  let my_method set el = Some [el]  (* whatever *)
  let show_el el = El.show el  (* we can use the "showable" elements! *)
end

Then we just need an OrderedShowable version of Int as the element type. Int is already ordered so we just have to extend it by deriving "show" and then we can make a concrete MySet:

module Int' = struct
  include Int
  type t = int [@@deriving show]
end

module MyIntSet = MySet_Make(Int')

And we can use it like:

# let myset = MyIntSet.of_list [3; 2; 8];;
# print_endline (MyIntSet.show_el 3);;
"3"