在 OCaml 中漂亮地打印一个 Hashtbl 以使用 ppx 派生

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

我正在尝试使用 ppx 派生在 OCaml 中漂亮地打印包含哈希表(使用 Base 标准库)的自定义记录类型,但我需要实现 Hashtbl.pp 才能正常工作。

我已经尝试查看在线示例,我找到的最好的示例是 https://github.com/ocaml-ppx/ppx_deriving#testing-plugins,但我仍然遇到奇怪的类型错误,例如 "This function has type Formatter.t -> (string, Value.t) Base.Hashtbl.t -> unit. It is applied to too many arguments; maybe you forgot a `;'"

如何使用 pp 函数扩展 Hashtbl 模块?

到目前为止,这是我的代码(Value.t 是我用 [@@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]

解决方案 1

您记录的 values 字段的类型是参数化的,具有两个类型变量,因此派生程序正在尝试使用由键和数据漂亮地参数化的通用 pp 函数-打印机,例如,以下将为任何散列table启用show(具有任何键和任何值,只要键和值是可显示的,

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

所以你终于可以定义你的类型了

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

方案二(推荐)

但是,我会建议另一种方法,而不是创建一个通用的 Hashtable 模块,而是创建一个专门的 Values 模块,例如,

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

现在您可以将其用作,

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

解决方案 3

如果你仍然想要一个通用的 printable 哈希 table,那么我建议不要使用 include 语句,而是只实现所需的 printable ('k,'s) Hashtbl.t 类型的接口,例如

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]