Ocaml 的 sprintf 不适用于 %a 格式说明符
Ocaml's sprintf doesn't work with %a format specifier
sprintf
似乎不允许在其格式说明符中使用 %a
。这是正确的吗?如果是这样,为什么会这样,有解决方法吗?
示例。我有一个又长又复杂的数据类型:
type t = Foo | Baz
我有一个适合这种类型的漂亮打印机,它将其值的字符串表示形式发送到任意 out_channel
:
let pp_t oc = function
| Foo -> fprintf oc "Foo"
| Baz -> fprintf oc "Baz"
如果我再写
let _ = printf "%a=%d" pp_t Foo 2
或
let _ = eprintf "%a=%d" pp_t Foo 2
然后一切正常——消息 "Foo=2" 最终出现在我的标准输出或我的标准错误中,正如预期的那样。但是如果我写
let s = sprintf "%a=%d" pp_t Foo 2
然后我得到一个编译错误,抱怨参数 pp_t
的类型为 out_channel -> t -> unit
但表达式应为 unit -> 'a -> string
类型。
是否根本不可能在 sprintf
的格式说明符中使用 %a
?
简答
使用 Format
模块进行漂亮的打印,使用 Format.asprintf
函数代替 sprintf
来构造字符串(我通常只在字符串的开头使用 open Format
文件)。
长答案
可以将 %a
转换规范与 sprintf
一起使用。但是,所需函数的类型不同于 Printf.fprintf
和 Printf.printf
所需的类型。
所有pretty-printers(满足%a
转换所需的值类型的函数)在其类型中编码输出类型。因此,对于不同的 printf
函数系列,您需要使用不同的 pretty-printing 函数。
对于 Format
模块 pretty-printers,您需要一个 formatter -> 'a -> unit
类型的函数。这是 top-level 和调试器也需要的类型,也是最常见的 pretty-printing 函数类型(Core 等库以及许多其他库提供 pretty-printers 满足这个签名)。 Format
模块还提供了 asprintf
函数,与 sprintf
函数不同,它可以很好地处理 %a
转换(即,它具有相同的 formatter -> 'a -> unit
类型)。
在 Printf.printf
的情况下,pretty-printer 的类型应该是 out_channel -> 'a -> unit
。这种类型的通用性要差得多,因此比 Format 的类型更不受欢迎。很少有图书馆提供。
最后,Printf.sprintf
函数需要 unit -> 'a -> string
类型的打印机。通常,获得如此漂亮的打印机的唯一方法是手动编写(有时使用 Printf.asprintf
)。在你的情况下它将是:
let pp_t () = function
| Foo -> "Foo"
| Baz -> "Baz"
为了避免出现在格式化函数中的特定数据类型,您应该始终使用 Format
模块并为具有签名 val pp_t : Format.formatter -> t -> unit
的类型设计漂亮的打印机;这也是顶层要求为您的数据类型安装带有 #install_printer
的自定义打印机的签名。
配备了这样的功能你可以简单地使用Format.asprintf
和"%a"
。
sprintf
似乎不允许在其格式说明符中使用 %a
。这是正确的吗?如果是这样,为什么会这样,有解决方法吗?
示例。我有一个又长又复杂的数据类型:
type t = Foo | Baz
我有一个适合这种类型的漂亮打印机,它将其值的字符串表示形式发送到任意 out_channel
:
let pp_t oc = function
| Foo -> fprintf oc "Foo"
| Baz -> fprintf oc "Baz"
如果我再写
let _ = printf "%a=%d" pp_t Foo 2
或
let _ = eprintf "%a=%d" pp_t Foo 2
然后一切正常——消息 "Foo=2" 最终出现在我的标准输出或我的标准错误中,正如预期的那样。但是如果我写
let s = sprintf "%a=%d" pp_t Foo 2
然后我得到一个编译错误,抱怨参数 pp_t
的类型为 out_channel -> t -> unit
但表达式应为 unit -> 'a -> string
类型。
是否根本不可能在 sprintf
的格式说明符中使用 %a
?
简答
使用 Format
模块进行漂亮的打印,使用 Format.asprintf
函数代替 sprintf
来构造字符串(我通常只在字符串的开头使用 open Format
文件)。
长答案
可以将 %a
转换规范与 sprintf
一起使用。但是,所需函数的类型不同于 Printf.fprintf
和 Printf.printf
所需的类型。
所有pretty-printers(满足%a
转换所需的值类型的函数)在其类型中编码输出类型。因此,对于不同的 printf
函数系列,您需要使用不同的 pretty-printing 函数。
对于 Format
模块 pretty-printers,您需要一个 formatter -> 'a -> unit
类型的函数。这是 top-level 和调试器也需要的类型,也是最常见的 pretty-printing 函数类型(Core 等库以及许多其他库提供 pretty-printers 满足这个签名)。 Format
模块还提供了 asprintf
函数,与 sprintf
函数不同,它可以很好地处理 %a
转换(即,它具有相同的 formatter -> 'a -> unit
类型)。
在 Printf.printf
的情况下,pretty-printer 的类型应该是 out_channel -> 'a -> unit
。这种类型的通用性要差得多,因此比 Format 的类型更不受欢迎。很少有图书馆提供。
最后,Printf.sprintf
函数需要 unit -> 'a -> string
类型的打印机。通常,获得如此漂亮的打印机的唯一方法是手动编写(有时使用 Printf.asprintf
)。在你的情况下它将是:
let pp_t () = function
| Foo -> "Foo"
| Baz -> "Baz"
为了避免出现在格式化函数中的特定数据类型,您应该始终使用 Format
模块并为具有签名 val pp_t : Format.formatter -> t -> unit
的类型设计漂亮的打印机;这也是顶层要求为您的数据类型安装带有 #install_printer
的自定义打印机的签名。
配备了这样的功能你可以简单地使用Format.asprintf
和"%a"
。