Ppx_deriving implementation for pretty printing sets & maps

Hi all,

Hoping for some casual coaching on basic printing of module types.

I want to print instances of the following, writing as little of my own formatters as possible.

module CharSet = Set.Make (Char)
module CharMap = Map.Make (Char)

In the Map case, clearly the value type is not speficified, but I’m happy to reify it into module with a concrete type of char option CharMap.t.

I already use ppx_deriving for my own types, so usage of that is preferred if it can be done concisely. Per ppx_deriving#working-with-existing-types it should be feasible, but I’m wresting enough to request assistance. Will be trying to grok the following threads in the interim.

Existing threads on adjacent topics:

2 Likes

I think the usual thing to do here is to convert your set/map to a list and then print that with [%derive.show: (char * value) list]

1 Like

Thank you. I’m so glad I asked!

module CharSet = Set.Make (Char)
module Signals = CharSet
(* snip *)
print_endline @@ [%show: char list] (Signals.elements signals);

Would be really slick if I could have

print_endline @@ [%show: Signals.elt list] (Signals.elements signals);
(* Unbound value Signals.pp_elt *)

Which… makes sense. Could be pretty slick if the some sort of derivation/lookup occurred and was substituted in once traversal detected types w/ adequate printers, but that seems like a pretty advanced ask/task. The type system perhaps hasn’t even started its great work by the time this codegen takes place. I suspect such a feature may not even be feasible.

I suspect all we’re really missing is the Set/Map functors implementing pp functions. If we had that, you’d be able to use [%show: Signals.t].

3 Likes

Will this work?

module Signals = struct
  include Set.Make(Char)

  type nonrec t = t [@derive.show]
  type nonrec elt = elt [@derive.show]

I may check it later

The containers stdlib extension makes this almost effortless:

open Containers

module Signals = Set.Make(Char)
let () = Format.printf "my set: %a" (Signals.pp Char.pp) @@ Signals.of_list ['a']

“Almost”, because the pp on Set, Map, etc needs to be parameterized by the value in question; but you can prefill that to make e.g. nested/recursive usage fit together nicely:

module Signals = struct
  module M = Set.Make (Char)
  include M

  let pp = M.pp Char.pp
end

let () =
  Format.printf "my set: %a" Signals.pp @@ Signals.of_list ['a']
2 Likes

Splendid. Containers tends to be my go-to stdlib ext, so this is particularly helpful for me :slight_smile:

1 Like

@vrotaru, no, it’s explicitly not supported:

While nonrec is not supported, a type alias like this wouldn’t work since it would just generate an alias for the other printer. For example:

type other = t [@@deriving show]

will only add:

let show_other = show
let pp_other = pp

To generate an actual definition, it needs access to the type definition (ppx_import won’t help here because the type is abstract)