在Ocaml中重用和扩展已定义的类型

11
在OCaml中,是否有一种简单的构造/风格来扩展已定义的类型? 比如说,如果我们有布尔类型。
bool2 = True | False 

现在我们想将它扩展为3值逻辑。在Ocaml中,是否有比像这样重新定义bool2更优雅的方法:

bool3 = True | False | ThirdOne
3个回答

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
假设bool2是库中定义的一种类型。显然,通过多态变量提出的方法需要我们将bool2的原始定义从单态转换为多态。因此,我想对我的问题进行简短而直接的回答:不,我们确实需要重新定义bool2才能将其扩展到bool3。对吧,马丁?谢谢。 - zell
哦,我在https://dev59.com/UXI-5IYBdhLWcg3wpqIK找到了一个非常相似的问题。 - zell

8
我建议不要过度使用多态变量。它们在纸上看起来很好,但更灵活的推断和子类型会在任何时候反咬你一口。当我使用多态变量时,我会尝试确保每个用法都带有精确的约束类型表达式的注释。
我建议回到修改您的旧代码,因为这似乎是自然而然的。如果您编写代码时考虑了可扩展性,并特别避免在bool2类型上使用_模式,则编译器将警告您进行任何假设只有两个构造函数的地方。这种类型修改的编译器反馈非常有用,因为它是使程序正确的机械帮助。
这种做法当然有一些缺点。其中一个问题是修改类型定义,然后修改每个使用案例可能不适合您通常的编译测试实践:如果您在大型代码库上执行此操作,则需要完成重要的事情,才能使项目再次成功编译(从而可以进行测试)。您可以将修改拆分为多个版本控制系统的补丁,但这意味着一些中间提交状态无法编译,这并不令人满意。另一种可能性是仅更改那些位置以添加运行时失败(| Third_one -> assert false),然后您就有了可编译的代码,并且可以在应用程序测试期间发生运行时故障时进行纠正。但我仍然认为静态编译器反馈对于代码维护是很有帮助的。
还有一种选择是将代数数据类型包装在“扩展代数数据类型”type bool3 = New | Old of bool2中,在Martin的答案评论中讨论了这一点。这可能是一种好的过渡方式,从一个数据类型转换到另一个数据类型而不会破坏编译,但从长远来看这是痛苦的,特别是如果您在其上堆叠更多的扩展。
当然,某些情况下真正需要的是一种通过代码添加而非修改来扩展数据类型的方法,以一种在静态安全、更易于编译、运行和测试以及运行时高效的方式。这是表达式问题的一个实例,其中有各种解决方案,多态变体是其中之一。但在普通情况下,你不需要仅执行代码添加的额外灵活性,并且它不值得担心语言功能的额外复杂性,因此我建议使用普通的旧变量类型,除非证明采用不同方式会带来巨大收益。
PS:关于多态变体及其与表达式问题的关系,必读的论文是Jacques Garrigue的通过多态变体实现代码重用

3

根据任务的不同,您可能会发现 可扩展变量类型 也很有用。更多信息和使用案例请点击此处

type bool = ..
type bool +=
    | True
    | False

(* Elsewhere *)
type bool +=
    | Third_one

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接