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"