How to ppx_show a module containing a custom record type (string, custom_record_type) Hashtbl.t

I have an program structured like the following:

module Thing = struct
  type t = {
    name : string;
    i : int;
  }
end

module Stuff = struct
   type t = {
      map : (string, Thing.t) Hashtbl.t;
      morestuff : string list;
    }
end

I’m trying to use ppx_show to derive a pretty printer for it, but it’s not working, and the error messages are extremely confusing. I tried

module Thing = struct
  type t = {
    name : string;
    i : int;
  }  [@@deriving show]
end

module Stuff = struct
   type t = {
      map : (string, Thing.t) Hashtbl.t [@printer fun fmt -> Hashtbl.iter (fun key thing -> fprintf fmt "%s = " key; Thing.pp fmt thing; fprintf fmt "\n")];
      morestuff : string list;
    }  [@@deriving show]
end

And I get unbound value Hashtbl.pp.

But when I try

module Thing = struct
  type t = {
    name : string;
    i : int;
  } [@@deriving show]
end 

module Hashtbl = struct
  include Hashtbl
  let pp = (* whatever *)
end

module Stuff = struct
   type t = {
      map : (string, Thing.t) Hashtbl.t;
      morestuff : string list;
    }  [@@deriving show]
end

Then however I try to define Hashtbl.pp, I get

Error: This function has type (* whatever -> whatever *)
       It is applied to too many arguments; maybe you forgot a `;'.

I’ve tried a bunch of different signatures and none of them seem to work, even if it only takes a Format.formatter and uses it to print <opaque> I still get this “too many arguments” error.

What am I doing wrong, and what’s the happy path for something like this?

The [@@deriving show] attributes should be before the end (otherwise they are outside of the module struct ... end) and you should use [@printer ...] (note: one @) instead of [@@deriving ...] to use a custom printer inside type Stuff.t.

Sorry, those are typos; I still get the same issue when using [@printer] and [@@deriving] in the right places.

Well, after your edit your first code block works on my machine. With the dune file:

(executable
 (public_name tmp)
 (name main)
 (preprocess (pps ppx_deriving.show))
 (libraries tmp))
module Thing = struct
  type t = {
    name : string;
  } [@@deriving show]
end 

module Stuff = struct
  type t = {
    map : (string, Thing.t) Hashtbl.t [@printer fun fmt map -> Hashtbl.iter (fun name thing -> fprintf fmt "%s = " name; Thing.pp fmt thing) map];
    thingnames : string list;
  } [@@deriving show]
end 

Here’s the precise code that’s not working on my machine. One thing that might be different is I’m using ppx_show instead of ppx_deriving.show because I heard that the latter was abandoned or not recommended or something and the former was a drop-in replacement. Maybe it’s not??

For what it’s worth, after installing ppx_deriving I’m now getting a different error with that same code block, which is

File "debug.ml", lines 10-13, characters 2-21:
10 | ..type t = {
11 |     map : (string, Thing.t) Hashtbl.t [@printer fun fmt map -> Hashtbl.iter (fun name thing -> fprintf fmt "%s = " name; Thing.pp fmt thing)];
12 |     thingnames : string list;
13 |   } [@@deriving show]
Error: This expression has type (string, Thing.t) Hashtbl.t -> unit
       but an expression was expected of type unit
       because it is in the left-hand side of a sequence

EDIT: Nvm, it does work, just had a typo in the printer function. Is ppx_deriving.show still the recommended preprocessor for deriving show functions then?? I had assumed that ppx_show’s lack of documentation was because it’s just a drop in replacement for ppx_deriving but it clearly isn’t! Even after fixing that typo, the code block still doesn’t work on my machine!

What is the modern community standard way of deriving printers through preprocessing?

I’ve always used ppx_deriving and never ppx_show, for the simple reason that it was what came up when I searched. I wasn’t aware that there was another option, so I won’t be able to answer here.

1 Like

First, your root cause problem is that Hashtbl doesn’t implement pp – it’s part of stdlib, and that stuff doesn’t support (rightly) all this show stuff. IIUC, the Hashtbl that’s part of Jane Street Core does support pp, so you might want to look into that.

But I don’t do that: I just use stdlib. So my solution is invariably to define a type

type t2 = (string * Thing.t) list [@@deriving show]

and then use t2_pp applied to a list (typically easy to get from a Hashtbl). And then that is what I stick in as the @printer for the field map.

I hope this is comprehensible.

I may state the obvious but rather than getting bogged down with stuff like that, you may also define your own string_of_type functions, something like this.:

let print (a, b) = print_string @@ Show.(tup2 (char_list a, char_list b))

It’s a bit of boilerplate but not that much work actually.

I’d do that if I’m just testing the waters.

(Show is my own module here)

Unfortunately I can’t use Core or any libraries really; I’m using OCaml for competitive programming / programming puzzles, and the site does not allow any external libraries. I can only get away with using deriving because preprocessor annotations are ignored by ocamlopt.

I think if I have time I might write a Community post here arguing that the standard library should have something like pp. IMO having a reasonable way of pretty printing stuff is essential for debugging, especially in OCaml, and its ridiculous that you need to switch out your standard library or keep a utilities file full of printers on standby just to be able to see the data you’re working with.

Thanks for the advice though!

1 Like

The problem is that even pp is fraught with dissension: do you want pp or pp_hum ? It isn’t clear.

You can always “add” pp to a type by redefining the module, viz.

module Hashtbl = struct include Hashtbl .... define pp here .... end

You don’t need to switch out your standard library. The ppx_deriving.* stuff is not a different standard library, it’s a bunch of macros and third-party runtime libraries.

I happen to agree that OCaml should have a built-in way of introspecting values at runtime. And we actually had a larger discussion about this a few months ago. Basically, the most general-purpose way of doing this would be to have a way of describing types at the term level so that different transformations could automatically be derived for the values, e.g. show, debug, JSON, Protobuf, SQL table, etc.

1 Like

IMO these are two separate-ish problems; ppx_deriving solves the problem of deriving printers for values composed of other values whose printers are already defined or trivial, but doesn’t help much when you’re using a container type like Map or Hashtbl that has no printer defined. OTOH, defining pp for Stdlib container types saves the work of writing the component printers but not the boilerplate of deriving and composing them.

(And then there’s the third completely separate problem of not having polymorphic show, which is really neither here nor there imo.)

I understand that there’s some controversy around standardizing in OCaml itself ppx_deriving.* equivalents and the best way of implementing it, but IMO a good first step that doesn’t require any fundamental changes to OCaml would just be to define a pp or something like that for each value and container type in the standard library. It’s just a Stdlib change; conceptually, you can think of it as just improving the API and discoverability of the already existing “Convenience Formatting Functions” and then adding a bunch more.

1 Like

I agree, this is a good idea. It’s worth following up on.

EDIT: many are already defined in OCaml library : Format

Looks like only a few are missing, e.g. Hashtbl, Queue.

1 Like