在Ocaml中,是否有一个简单的构造/样式来扩展定义的类型?
说,如果我们有布尔类型
bool2 = True | False
现在我们想将它扩展为3值逻辑。在Ocaml中,比重新定义bool2更优雅:
bool3 = True | False | ThirdOne
答案 0 :(得分:14)
多态变体提供此功能:
type bool2 = [ `True | `False ]
type bool3 = [ bool2 | `Third_one ]
就是这样。
多态变体还有另一个有用的快捷方式。在模式匹配中,使用前面带有#
的类型名称:
let is_bool2 = function
#bool2 -> true
| `Third_one -> false
应谨慎使用多态变体,因为它们很容易导致混淆错误消息。如果原始类型bool2
无论如何都不是多态变体,则如下实现两种类型的并集。我们假设bool2
是核心类型bool
,我们使用经典变体的定义是:
type bool3 = Bool of bool | Third_one
在模式匹配中,编译器将检查是否覆盖了所有情况,而不需要类型注释。它看起来像这样:
match x with | Bool true -> ... | Bool false -> ... | Third_one -> ...
答案 1 :(得分:8)
我建议不要过度使用多态变体。它们在纸上看起来很漂亮,但更灵活的推理和子类型会随时让你反感。当我使用多态变体时,我会尝试确保每个用法都使用精确的约束类型表达式进行注释。
我建议回去修改你的旧代码,因为它似乎很自然。如果您已经编写了具有可扩展性的代码,特别是_
- bool2
类型上的模式,那么编译器将警告您任何假设只有两个构造函数的地方是制作。这个编译器对类型修改的反馈是非常有用的,因为它是一个机械帮助,可以使你的程序正确。
这种做事方式当然有一些缺点。其中之一是修改类型定义,然后修改每个用例可能不适合您通常的编译测试实践:如果您在大型代码库上执行此操作,则在项目编译之前您将需要执行大量操作再次干净(因此可以测试)。您可以将修改分为几个补丁到您的版本控制系统,但这意味着一些中间提交的状态不进行编译,这不是很令人愉快。另一种可能性是仅更改这些位置以添加运行时故障(| Third_one -> assert false
),然后您具有可编译代码,并且可以在应用程序测试期间在运行时更正这些故障。但我仍然认为静态编译器反馈对代码维护是一个很好的帮助。
还可以选择将代数数据类型包装在“扩展代数数据类型”type bool3 = New | Old of bool2
中,这在您作为Martin答案的注释提供的链接中进行了讨论。这可能是在不破坏编译的情况下从一种数据类型转换到另一种数据类型的好方法,但从长远来看,这很痛苦,特别是如果您将更多这些扩展堆叠在一起之上。
当然,在某些情况下真正需要的是一种通过代码添加而不是代码修改来扩展数据类型的方法,其方式既安全,更易于编译,运行和测试,并且高效在运行时。这是Expression Problem的一个实例,它们是各种解决方案,多态变体就是其中之一。但是在常见的情况下,你不需要额外的灵活性来进行代码添加,并且它不值得相关语言功能的额外复杂性,所以我建议坚持使用普通的旧变体类型,除非它显然是巨大的不同的做法。
PS:关于多态变体及其与表达问题的关系,Jacques Garrigue的强制性论文是Code reuse through polymorphic variants。
答案 2 :(得分:2)
根据手头的任务,您可能还会发现Extensible Variant Types有用。更多信息和用例here。
type bool = ..
type bool +=
| True
| False
(* Elsewhere *)
type bool +=
| Third_one