使用其他方法制作Ocaml子类的正确方法是什么?

时间:2011-02-06 17:07:37

标签: class inheritance object types ocaml

在Ocaml中,我正在努力进行子类化和类型化:

class super =  
  object (self)  
  method doIt =   
    ...  
end;  

class sub =  
  object (self)  
  inherit super  
  method doIt =   
    ...  
    self#somethingElse  
    ...  

  method somethingElse =  
    ...  
end;  

let myFunction (s:super) =  
  ...  

myFunction new sub

显然在Ocaml中,类sub 是类super的“子类型”,因为sub#doIt方法调用sub中的方法} super中没有。但是,这似乎是OO编程的一个非常常见的用例。推荐的方法是什么?

3 个答案:

答案 0 :(得分:8)

sub可能是super的子类型。但是在ocaml中没有隐式类型转换。所以你的函数不接受super的子类型。你必须明确强制:

let myFunction (s:super) =  
  ...  

myFunction (new sub :> super)

或者最好接受super的子类型:

let myFunction (s:#super) =  
  ...  

myFunction new sub

答案 1 :(得分:8)

正如Rémi所提到的,代码的问题在于OCaml类型系统每个表达式只支持一种类型:类型sub的表达式不是super类型。在您的示例中,myFunction期望参数类型为super,而表达式new sub的类型为sub,因此存在问题。

向上转换对于面向对象的编程至关重要,而OCaml确实通过两种不同的结构来支持它。

第一种是类型强制。如果supersub的超类型(意味着在语义上,类型sub的值表现为super)和x : sub的值,那么{{1 }}。 (x :> super) : super类型运算符使转换显式化 - 相当于当您使用:>类型的avalue时,流行的面向对象语言会隐式执行此操作。

第二个是超类型约束:要求给定的类型变量是给定类型的子类型。如果您希望在其中命名类型变量,则将其写为subsuper。超类型约束实际上并没有像强制类型那样改变表达式的类型,它们只是检查类型是否是所需类型的有效子类型。

要更加了解差异,请考虑以下示例:

#super

(#super as 'a)的类型为class with_size ~size = object val size = size : int method size = size end class person ~name ~size = object inherit with_size ~size val name = name : string method name = name end let pick_smallest_coerce (a : with_size) (b : with_size) = if a # size < b # size then a else b let pick_smallest_subtype (a : #with_size) (b : #with_size) = if a # size < b # size then a else b :即使您通过了两个pic_smallest_coerce个实例,返回值也是with_size -> with_size -> with_size类型,您将无法调用它的person方法。

with_size的类型为name:如果您传递了两个pic_smallest_subtype个实例,则类型系统会确定(#with_size as 'a) -> 'a -> 'a并正确地将返回值标识为类型person(允许您使用'a = person方法)。

简而言之,超类型约束只是确保您的代码能够运行,而不会丢失任何类型信息 - 变量保留其原始类型。类型强制实际上会丢失类型信息(在没有下载的情况下,这是非常讨厌的事情),所以它应该只在两种情况下用作最后的手段

1。您不能拥有多态功能。超类型约束依赖于person是一个自由类型变量,所以如果你不能在你的代码中有一个自由类型变量,你将不得不这样做。

2. 您需要在同一容器中实际存储不同实际类型的值。可以包含name#super个实例的列表或引用将使用person和强制:

box

请注意,类型推断算法将自己发现超类型约束(它不会确定您要使用的类或类类型,但它将构造一个文字类类型):

with_size

因此,除了极少数例外,仅在记录代码时注释实际的超类型约束才是有用的练习。

答案 2 :(得分:0)

如果您希望myFunction接受super的任何子类型作为参数,那么您应该像这样定义:

let myFunction s =
  let s = (s :> super) in
  ...

...或等效......

let myFunction (s :> super) =
  ...

至于您的评论sub在您的示例中似乎不是super的子类型,因为doIt中的sub方法是在对象值时调度的内容#运算符左侧的类型为sub,嗯,我认为你错了。这正是你应该期待的。

要允许sub类中的方法调用doIt类中的super方法,您应该像这样定义它们:

class super = object (self)  
  method doIt =   
    ...  
end;  

class sub = object (self)  
  inherit super as parent

  method doIt =   
    ...
    let _ = parent#doIt in
    ...
    self#somethingElse  
    ...  

  method somethingElse =  
    ...  
end;