什么时候应该在 OCaml 中使用对象?

Posted

技术标签:

【中文标题】什么时候应该在 OCaml 中使用对象?【英文标题】:When should objects be used in OCaml? 【发布时间】:2012-06-02 12:05:51 【问题描述】:

通常,OCaml 程序可以使用或不使用对象来编写。什么时候使用对象最有利,什么时候应该避免使用?

【问题讨论】:

【参考方案1】:

一般来说,不要使用对象。它们带来的额外复杂性通常并不值得。我认为这也是适用于其他语言的规则,但这是另一回事。至少对于 OCaml,人们可以客观地(不是双关语)说通常的做法是不使用对象,除非在极少数情况下。

对象提供一束:

    一种“标准”风格,用于携带和使用函数记录,可能具有多态类型 通过self(实现继承)进行开放递归的工具 具有行多态性(用于开放对象类型)和子类型(用于封闭对象类型)的结构化、可扩展产品类型

您可以一起使用,也可以单独使用。

根据我的经验,仅第 (1) 点并不特别值得使用对象:您可以只使用函数的记录,它同样清晰。

开放递归/继承的用例

相反,第 (2) 点是使用面向对象风格的一个很好的理由;例如,Camlp4 以这种方式使用它:Camlp4 定义了折叠 AST 的类,什么都不做,您可以继承此遍历对象以仅在您想要的句法结构上实现您想要的行为(并将无聊的遍历管道推迟到你的妈妈班)。

例如,可以扩展Camlp4Ast.map 对象,它在 OCaml 抽象语法树的 Camlp4 表示上定义了一个简单的映射函数,只是将每个构造递归地映射到自身。如果你想,比如说,将所有(fun x -> e1) e2 表达式映射到let x = e2 in e1,你从这个对象继承,并覆盖expr 方法,只处理你想要的情况(左侧是一个函数),委托另一个是继承的行为。这将为您提供一个对象,该对象知道如何递归地在完整程序上应用此转换,而无需编写任何样板代码;如果您愿意,您可以通过其他行为进一步扩展此转换。

对象类型的乐趣

第 (3) 点也是将对象用作“可扩展记录”或“类型级数组”的理由;一些库使用对象类型,但在运行时不使用对象:它们使用对象类型作为幻像类型来携带信息,受益于您可以对对象进行的更丰富的类型级操作。此外,结构类型允许不同的作者拥有兼容的类型,而不会强烈依赖于定义他们共享的(名义)类型的公共组件;例如,对象已用于输入/输出组件的标准化。

一个不常见的、非常简单的用例是表示具有大量参数的类型的惯用方式。而不是写:

type ('name, 'addr, 'job, 'id) person = ....
val me : (string, string, Job.t, Big_int.big_int) person

您可以使用对象类型作为结构“类型级记录”来代替:

type 'a person = .... constraint 'a = < name:'n; addr:'a; job:'j; id:'i >
val me : < name:string; addr:string; job:Job.t; id:Big_int.big_int > person

要更高级地使用对象类型作为幻像类型,您可以查看 Alec 的 ShCaml (doc) 库(用于表示字符串输入与哪些 shell 命令兼容) Heller 和 Jesse Tov,或者我自己的 Macaque 库(doc 和 api doc),它使用对象类型来表示 SQL 值(包括可空性信息)和表行类型。 多态变体(OCaml 类型系统的另一个高级特性;一句话,对象和记录之间的关系与多态变体和代数和类型之间的关系相同)。也被用作幻像类型,例如in this simple example by Richard Jones,或在Ocsigen 框架中检查 html 文档的有效性。

但请注意,这些高级类型的黑客会付出巨大的复杂性成本;在使用它们之前,您必须仔细平衡它与它们带来的额外表现力和静态安全性。

总结

作为一个基本假设,完全不使用对象是安全的;如果您觉得自己缺少某些东西,您应该只在您的设计中引入它们,而不是默认情况下

对象便于开放递归/继承:改进在默认/无聊情况下已经定义的行为

当您想要独立地对值进行推理时,结构类型有时会很有用,以提供一组特性/能力

【讨论】:

非常彻底的答案。非常感谢! @gasche 我认为您的Camlp4Ast.map 链接有问题。 谢谢,我修复了链接。 “结构类型允许不同的作者拥有兼容的类型,而不需要强依赖于一个公共组件。” OCaml 记录实现了结构类型,不是吗? 不,OCaml 记录是生成的(或者,名义上是类型的):具有相同字段的两个记录声明不兼容。 SML 记录是结构化的,但不可扩展,因此更难以有效的方式编译/执行。

以上是关于什么时候应该在 OCaml 中使用对象?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 `id id` 在 OCaml 中不是一个值?

如何在 OCaml 中交错 3 个列表

“let () =”在 Ocaml 中是啥意思?

OCaml:在另一个内部匹配表达式?

在 OCaml 中的 let 命令(即 let _ = ... in)中使用下划线通配符有啥副作用吗?

什么是模拟,什么时候应该使用它?