Search code examples
ocamlppx

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

  • 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"