是否有一个函数可以表示任何类型的字符串?
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。
我一直在拼命寻找 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。