通常,OCaml程序可以使用或不使用对象编写。何时使用物体最有利,何时应避免使用?
答案 0 :(得分:73)
作为一般经验法则,请勿使用对象。他们带来的额外复杂性往往不值得。我认为这也是适用于其他语言的规则,但这是另一个故事。至少对于OCaml,人们可以客观地(没有双关语)说通常的做法是不使用对象,除非在极少数情况下。
对象提供了一个包:
self
(实施继承)开放递归的设施您可以一起使用,也可以单独使用。
根据我的经验,单独使用point(1)并不是特别值得使用对象:你可以只使用函数记录,它也很清楚。
相反,第(2)点是使用面向对象风格的一个很好的理由;例如,Camlp4以这种方式使用它:Camlp4定义了折叠AST的类,什么都不做,你可以继承这个遍历对象,只在你想要的语法结构上实现你想要的行为(并且将枯燥的遍历管道推迟到你的母班)。
例如,可以扩展Camlp4Ast.map对象,它在OCaml抽象语法树的Camlp4表示上定义一个简单的映射函数,只是递归地将每个构造映射到它自己。如果您想将所有(fun x -> e1) e2
表达式映射到let x = e2 in e1
,则继承此对象,并覆盖expr
方法,仅处理您想要的情况(左侧)是一个函数),将另一个委托给继承的行为。这将为您提供一个对象,该对象知道如何以递归方式对整个程序应用此转换,而无需编写任何样板代码;如果您愿意,可以使用其他行为进一步扩展此转换。
Point(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
为了更高级地使用对象类型作为幻像类型,您可以查看ShCaml(doc)库(它用于表示字符串输入兼容的shell命令) with)由Alec Heller和Jesse Tov,或我自己的Macaque库(doc和api doc),它使用对象类型来表示SQL值(包括可空性信息)和表行类型。
多态变体(OCaml类型系统的另一个高级特征;在一个句子中,对象和记录之间的关系与多态变体和代数和类型之间的关系相同)。还被用作幻像类型,例如{{3}理查德琼斯,或检查in this simple example框架中的HTML文档的有效性。
请注意,那些先进型hackeries的成本非常高;在使用之前,您必须仔细权衡它们带来的额外表现力和静电安全性。
作为基本假设,你根本不使用对象是安全的;你应该只在你的设计中介绍它们,如果你觉得你错过了什么,而不是默认
对象便于打开递归/继承:改进已在默认/无聊案例中定义的行为
当您想要独立提供一组功能/容量来推理值时,结构类型偶尔会很有用