是否有一个函数可以表示任何类型的字符串?

Is there a function that can make a string representation of any type?

我一直在拼命寻找 OCaml 库中将 'a 转换为字符串的方法的最后一个小时:

'a -> string 图书馆里有什么我还没找到的东西吗?还是我必须做不同的事情(我自己写所有的东西)?

无法在 OCaml 中编写 'a -> string 类型的打印函数 show

确实,在 OCaml 中编译后类型被擦除。 (实际上,在编译管道的早期阶段之一的类型检查之后,它们被删除了)。

因此,'a -> _ 类型的函数可以:

  • 忽略它的参数:
let f _ = "<something>"
  • 查看值的内存表示
let f x = if Obj.is_block x then "<block>" else "<immediate>"

即使查看一个值的内存表示也没有多大用处,因为许多不同的类型将共享相同的内存表示。

如果你想打印一个类型,你需要为这个类型创建一个打印机。您可以使用 Fmt 库(或标准库中的 Format 模块)手动执行此操作

type tree = Leaf of int | Node of { left:tree; right: tree } 
let pp ppf tree = match tree with 
| Leaf d -> Fmt.fp ppf "Leaf %d" d
| Node n -> Fmt.fp ppf "Node { left:%a; right:%a}" pp n.left pp n.right 

或使用 ppx(OCaml 的小型预处理扩展),如 https://github.com/ocaml-ppx/ppx_deriving

type tree = Leaf of int | Node of { left:tree; right: tree } [@@deriving show]

您正在寻找的是 有意义的 类型 'a. 'a -> string 函数,具有参数多态性(即可以操作相同 的单个函数对于所有 可能的类型 'a,甚至是那些在创建函数时不存在的类型)。这在 OCaml 中是不可能的。以下是根据您的编程背景进行的解释。

来自Haskell

如果您因为熟悉 Haskell 函数 show 而期待这样的函数,请注意它的类型实际上是 show :: Show a => a -> String。它使用类型 class Show a 的实例,它由编译器隐式插入到调用点。这不是参数多态性,这是临时多态性(如果需要,show 已重载)。 OCaml 中没有这样的功能(但是?有语言未来的项目,寻找“模块化隐式”或“模块化显式”)。

来自 OOP

如果您因为熟悉 OO 语言而期待这样的功能,其中每个值都是一个具有方法的对象 toString,那么 OCaml 就不是这种情况。 OCaml 没有普遍使用对象模型,并且 运行-OCaml 值的时间表示不保留(或很少)类型的概念。我建议你参考@octachron 的回答。

同样,OOP 中的 toString 不是参数多态性而是重载:没有为 所有 可能类型定义的单一方法 toString。取而代之的是同名方法的多个(可能非常不同)实现。在某些 OO 语言中,程序员尝试遵循为他们定义的每个 class 实现该名称的方法的原则,但这只是一种编码实践。可以很好地创建没有这种方法的对象。

[ 实际上,两个世界涉及的概念非常相似:Haskell 需要一个类型的实例class Show a 提供一个函数show; OOP 需要 class Stringifiable 的对象(例如)提供方法 toString。或者,当然,后代 typeclass/class 的 instance/object。 ]

如果你只是想要一个快速的 hacky 解决方案,你可以使用 dump from theBatteries 库。它不适用于所有情况,但它确实适用于原语、列表等。它访问底层原始内存表示,因此能够(在某种程度上)克服其他答案中提到的困难。

你可以这样使用它(通过opam install batteries安装后):

# #require "batteries";;
# Batteries.dump 1;;
- : string = "1"
# Batteries.dump 1.2;;
- : string = "1.2"
# Batteries.dump [1;2;3];;
- : string = "[1; 2; 3]"

如果您想要更“合适”的解决方案,请使用@octachron 推荐的ppx_deriving。 reliable/maintainable/customizable.

另一种可能性是使用 https://github.com/ocaml-ppx/ppx_deriving 将创建 Path.To.My.Super.Type.t -> 字符串的函数,然后您可以将其与您的值一起使用。然而,您仍然需要手动跟踪类型的路径,但总比没有好。

另一个项目提供了类似于 Batterie 的功能 https://github.com/reasonml/reason-native/blob/master/src/console/README.md(我没有测试 Batterie 所以不能给出意见)他们有同样的限制:他们内省运行时编码所以不能得到真正有用的东西.我认为这是在考虑到 windows/browser 的情况下完成的,因此如果需要交叉平台,我会先测试这个(除非电池已经拔出)。即使代码源合理,您也可以在 OCaml 中使用相同的 API。