为什么OCaml不支持记录子类型?

15

阅读《类型和编程语言》,我对使用闭包和记录子类型实现对象印象深刻(第18章)。有没有特殊的原因,导致OCaml不支持记录子类型(虽然我知道对象支持)?事实上,我找不到任何支持此功能的语言。


2
SML# 具有记录子类型。OCaml 的对象是带有子类型的记录。结构子类型很强大,但其类型错误通常很难理解。因此,OCaml 用户通常更喜欢没有子类型的简单记录。 - camlspotter
谢谢指出SML#! SML#中的记录子类型与OCaml中的对象结构类型是否有区别? - Olle Härstedt
2个回答

23
从技术上讲,OCaml的对象并不真正支持通常意义下的子类型,而是采用了行多态性。使用行多态性相比子类型有很多优势,特别是它既更具表现力,又与类型推导更加协调(子类型和类型推导完全不搭配)。但是,将结构子类型或行多态性应用于所有记录的主要问题在于它需要一个更为复杂的运行时实现,并且因此成本更高。简单的记录可以轻松地转换为普通元组,字段访问只需要索引即可。而结构子类型或行多态性则需要能够透明地“切片”一个对象,即以随机字段被移除的超类型视图查看它。一般来说,这要么需要通过哈希查找字段(例如Ocaml的对象),要么需要利用“证据传递”技术,在函数或其任何被调用者中使用的每个字段的索引都必须作为隐藏参数传递给实际记录之外(例如SML#就是这样做的)。无论如何,Ocaml确实具有多态记录,它们只是被称为对象。但如果您不需要,可以忽略它们周围的所有类混乱。

1
是的,类的混乱让我失去了兴趣。我对OCaml还很新,但我期待着更多地了解它的对象系统。无论如何,谢谢你的回答! - Olle Härstedt

9
记录子类型在类型学的角度来看是相当危险的。假设您有一个包含三个字段a,b和c的记录。现在您想要创建一个只包含a和c两个字段的记录。编译器无法知道您使用的类型,直到它完成对整个记录的读取。最终,在没有完全读取记录的情况下,如果您犯了错误(例如忘记了b字段),编译器也无法提供帮助。
我完全认为这种观点是值得争议的,但我认为这就是编写OCaml代码的人们的想法。

谢谢您指出这一点。我想点赞,但我的声望太低了;) - Olle Härstedt
3
嗯,我不太明白。子类型化意味着您可以在期望2个字段记录的位置使用一个具有3个字段的记录。但在您的示例中,您创建了一个可能是错误的2个字段记录。即使使用子类型化,编译器也只会允许您在实际期望2个字段(或更少)的位置使用它。但这没关系,因为那些位置甚至不会看到您的第三个字段。 - max

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