为什么这里不调用 mark stag 函数?

Why mark stag functions are not called here?

我试图理解 OCaml Format 模块和语义标签的以下行为。

我的代码:

let prepare_ppf ppf =
  let original_stag_functions = Format.pp_get_formatter_stag_functions ppf () in
  let original_mark_tags_state = Format.pp_get_mark_tags ppf () in
  Format.pp_set_mark_tags ppf true;
  Format.pp_set_print_tags ppf false;
  Format.pp_set_formatter_stag_functions ppf {
    mark_open_stag = (fun stag ->
          print_endline "MARK-OPEN";
          match stag with
          | Format.String_tag s -> Printf.sprintf "<open:%s>" s
          | _ -> "<UNKNOWN>"
        );
    mark_close_stag = (fun stag ->
          print_endline "MARK-CLOSE";
          match stag with
          | Format.String_tag s -> Printf.sprintf "</close:%s>" s
          | _ -> "</UNKNOWN>"
        );
    print_open_stag = (fun _ -> print_endline "PRINT-OPEN"; ());
    print_close_stag = (fun _ -> print_endline "PRINT-CLOSE"; ());
  };
  print_endline "PREPARED";
  if Format.pp_get_mark_tags ppf () then print_endline "MARK:true";
  (fun ppf ->
      print_endline "RESET";
      Format.pp_set_mark_tags ppf original_mark_tags_state;
      Format.pp_set_formatter_stag_functions ppf original_stag_functions;)

let fprintf ppf fmt =
  let reset = prepare_ppf ppf in
  Format.kfprintf reset ppf fmt

let printf fmt = fprintf Format.std_formatter fmt

如果我将其粘贴到:utop 2.8.0 版(使用 OCaml 4.12.0 版)

当我运行它时:

utop # printf "@{<bold>%s@}" "hello";;
PREPARED
MARK:true
RESET
<bold>hello</bold>- : unit = ()

为什么 mark_open_stag 和关闭函数没有被调用?

如果我将第 5 行更改为 Format.pp_set_print_tags ppf true;,那么我会看到调用了 print_open_stag 和关闭函数。

这是标准输出格式化程序的缓冲和 utop 处理之间的交互。

可以通过

看到缓冲问题
printf "@{<bold>%s@}" "A very very very very very very very very very very very very very very very very very long hello world";;

打印出一半正确的结果

PREPARED
MARK:true
MARK-OPEN
<open:bold>A very very very very very very very very very very very very very very very very very long hello worldRESET
</bold>

更进一步,最后用

刷新标准输出
printf "@{<bold>%s@}@." "hello";;

产生正确的输出

PREPARED
MARK:true
MARK-OPEN
<open:bold>helloMARK-CLOSE
</close:bold>
RESET

问题是

printf "@{<bold>%s@}" "hello"

完全缓冲所有输入。 它是 utop 牵手 stdout 格式化程序,它通过尝试打印

来触发打印
- : unit = ()

这会产生

<bold>hello</bold>- : unit = ()

因为在打印时 utop 已将格式化程序配置重置为其默认值。