Samarth
Samarth

Reputation: 43

Pretty-print a Hashtbl in OCaml to work with ppx-deriving

I'm trying to pretty print a custom record type that contains a Hashtable (using the Base standard library) in OCaml with ppx-deriving, but I need to implement Hashtbl.pp for it to work.

I've tried looking at examples online and the best one that I found is https://github.com/ocaml-ppx/ppx_deriving#testing-plugins, but I'm still getting strange type errors like "This function has type Formatter.t -> (string, Value.t) Base.Hashtbl.t -> unit. It is applied to too many arguments; maybe you forgot a `;'"

How do you extend the Hashtbl module with a pp function?

Here is my code so far (Value.t is a custom type which I successfully annotated with [@@deriving show]:

open Base

(* Extend Hashtbl with a custom pretty-printer *)
module Hashtbl = struct
  include Hashtbl

  let rec (pp : Formatter.t -> (string, Value.t) Hashtbl.t -> Ppx_deriving_runtime.unit) =
   fun fmt -> function
    | ht ->
      List.iter
        ~f:(fun (str, value) ->
          Caml.Format.fprintf fmt "@[<1>%s: %s@]@." str (Value.string_of value))
        (Hashtbl.to_alist ht)

  and show : (string, Value.t) Hashtbl.t -> Ppx_deriving_runtime.string =
   fun s -> Caml.Format.asprintf "%a" pp s
 ;;
end

type t =
  { values : (string, Value.t) Hashtbl.t
  ; enclosing : t option
  }
[@@deriving show]

Upvotes: 4

Views: 803

Answers (1)

ivg
ivg

Reputation: 35260

Solution 1

The type of the values field of your record is a parametrized with two type variables, therefore the deriver is trying to use a general pp function that is parametrized by the key and data pretty-printers, e.g., the following will enable show for any hashtable (with any key and any value, as long as keys and values are showable,

module Hashtbl = struct
  include Base.Hashtbl

  let pp pp_key pp_value ppf  values =
    Hashtbl.iteri values ~f:(fun ~key ~data ->
      Format.fprintf ppf "@[<1>%a: %a@]@." pp_key key pp_value data)
end

so you can finally define your type

type t = {
  values : (string,Value.t) Hashtbl.t;
  enclosing : t option;
} [@@deriving show]

Solution 2 (recommended)

However, I would suggest another approach that instead of creating a general Hashtable module, creates a specialized Values module, e.g.,

module Values = struct
  type t = (string, Value.t) Hashtbl.t

  let pp ppf values =
    Hashtbl.iteri values ~f:(fun ~key ~data ->
      Format.fprintf ppf "@[<1>%s: %s@]@." key (Value.to_string data))
end

Now you can use it as,

type t = {
  values : Values.t;
  enclosing : t option;
} [@@deriving show]

Solution 3

If you still want a generic printable hash table, then I would advise against using the include statement, but, instead, implement just the required printable interface for the ('k,'s) Hashtbl.t type, e.g.,

module Hashtbl_printable = struct
  type ('k,'s) t = ('k, 's) Hashtbl.t

  let pp pp_key pp_value ppf  values =
    Hashtbl.iteri values ~f:(fun ~key ~data ->
      Format.fprintf ppf "@[<1>%a: %a@]@." pp_key key pp_value data)
end

type t = {
  values : (string, Value.t) Hashtbl_printable.t;
  enclosing : t option;
} [@@deriving show]

Upvotes: 4

Related Questions