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.
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.
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 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.
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.
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.
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.