在 ocamldebug 自定义打印机中打印枚举类型和记录类型

Printing enumerated and record types within an ocamldebug custom printer

我正在尝试为 Map 类型创建自定义打印机,以便在 ocamldebug 中使用。我想在我的自定义打印机中将调试器的漂亮打印机用于枚举和记录类型。这可能吗?

我有一个模块,m.mli,有几个类型,我正在将整数映射到 gammas:

type alpha = A | B
type gamma = { m: alpha }
module IMap : Map.S with type key = int
val print_map: gamma IMap.t -> unit

实现看起来像这样:

open Format

type alpha = A | B
type gamma = { m: alpha }

module IMap = Map.Make(struct 
  type t = int 
  let compare = (-) 
end)

let rec ig_pairs = function
  | [] -> ()
  | [(k,g)] ->
     open_hvbox 0;
     printf "%u->" k;
     print_break 0 2;
     print_string (stringifier_that_saves_my_ass g);
     close_box ()
  | (k,g)::xs ->
     open_hvbox 0;
     printf "%u->" k;
     print_break 0 2;
     print_string (stringifier_that_saves_my_ass g);
     print_string ",";
     close_box ();
     ig_pairs xs;
     ()

let print_map m =
  print_string "{";
  open_hvbox 0;
  ig_pairs (IMap.bindings m);
  close_box ();
  print_string "}"

最后,我有 script.ml:

open M

let key = 2
let some_gamma_value = { m = B }
let old = IMap.empty
let b = IMap.add key some_gamma_value old

使用 ocamldebug 单步执行我的脚本(在存根 stringifier_that_saves_my_ass 之后),

  1. 我可以 print some_gamma_value 获得 some_gamma_value
  2. 的一个很好的表示
  3. 我可以 load_printer m.cmoinstall_printer print_mapprint b 中得到 <abstr> 以外的东西。

我希望 stringify_that_saves_my_ass 在我的自定义打印机中提供来自 (1) 的表示,以便于打印地图值。

要安装在 ocamldebug 或 ocaml 顶层,打印机需要具有以下签名:

Format.formatter -> t -> unit

其中 t 是您要打印的类型。

这意味着,您的打印机还应该接受一个参数,即格式化程序,即通道的抽象。也就是说,不是只输出到标准输出,而是需要输出到指定的通道。这意味着,您需要使用 fprintfpp_ 接受格式化程序的函数系列。

关于 stringifier 问题,因为你的映射在值类型上是多态的,你需要让你的打印机成为一个函数,从打印你的值类型的打印机到打印整个地图。这只是意味着,您需要向打印机添加一个额外的参数,即 pp_value,它将打印值类型。

为了在一个完整的示例中演示这一点,请查看此 Trie(我忘记从中删除调试代码)模块,

let rec pp pp_val fmt t =
    let pp_some_data fmt = function
      | None -> ()
      | Some v -> fprintf fmt "data =@ %a@," pp_val v in
    let pp_table fmt cs =
      Tokens.iter cs (fun ~key ~data ->
          let toks = Key.sexp_of_token key in
          fprintf fmt "@[%a ->@ %a@]"
            Sexp.pp toks (pp pp_val) data) in
    fprintf fmt "{@;@[%a@ %a@]}@;"
      pp_some_data t.data pp_table t.subs

尝试是稍微复杂一点的地图,所以打印机反映了这一点,但让我们来看看打印机的用法。但是在我们需要将 Trie 具体化为特定的键类型之前,让它成为 string

module String = Make(struct
    type t = string
    type token = char with bin_io, compare, sexp
    let length = String.length
    let nth_token = String.unsafe_get
    let token_hash = Char.to_int
  end)

现在打印出来。同样,我们只能打印具体结构,所以假设我们要使用 int Trie,即我们的值类型是 int。我们为 int 次尝试创建了一台打印机,并安装了它

let pp_int_trie = pp pp_print_int
#install_printer pp_int_trie;;

之后,将打印所有具有 int 值类型的字符串尝试,完整示例如下:

open String
let pp_int_trie = pp pp_print_int
let t = create ();;
add t "hell is my life" 1;;
add t "hello" 2;;
add t "hello my darling Clementine" 3;;
#install_printer pp_int_trie;;
t;;

我希望这足以理解这个想法。

Deriving 库提供预处理器,可以自动为记录和变体类型构建格式化程序。您只需为类型添加后缀 with show(或 deriving (Show))。生成的格式化程序要求任何包含的类型也都带有 with show 后缀。给定 type a = A | B with show,预处理器生成 module Show_a = ... 使得 Show_a.format: Format.formatter -> t -> unit。这个 format 函数可以很好地替代调试器的漂亮打印机。

您可以 with show-后缀 Map 也可以省去编写 print_map 方法的麻烦。