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

Dav*_*ind 8 inheritance ocaml types class object

在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
Run Code Online (Sandbox Code Playgroud)

显然OCaml中,阶级sub不是一类的"子" super,因为该sub#doIt方法调用的方法sub是不存在的super.但是,这似乎是OO编程的一个非常常见的用例.推荐的方法是什么?

Vic*_*let 9

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

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

首先是类型强制.如果super是超类型sub(意味着在语义上,类型的sub值表现为值super)x : sub,那么(x :> super) : super.该:>类型的操作,使转换明确的-当你使用类型的安勤哪些流行的面向对象语言含蓄地做相当于sub其中super的预期.

第二种是超类型约束:要求给定的类型变量是给定类型的子类型.这被写成#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
Run Code Online (Sandbox Code Playgroud)

类型pic_smallest_coercewith_size -> with_size -> with_size:即使您传递了两个person实例,返回值也是类型with_size,您将无法调用其name方法.

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

简而言之,超类型约束只是确保您的代码运行,而不会丢失任何类型信息 - 变量保留其原始类型.类型强制实际上会丢失类型信息(在没有向下转换的情况下,它是一个非常讨厌的东西),所以它应该只在两种情况下作为最后的手段:

1.你不能拥有多态函数.超类型约束依赖于#super自由类型变量,因此如果您无法承担代码中的自由类型变量,则必须不使用它.

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

let things = [ my_person :> with_size ; my_box :> with_size ]
Run Code Online (Sandbox Code Playgroud)

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

let pick_smallest_infer a b = 
  if a # size < b # size then a else b

val pick_smallest_infer : (< size : 'a ; .. > as 'b) -> 'b -> 'b
Run Code Online (Sandbox Code Playgroud)

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


Rém*_*émi 8

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

let myFunction (s:super) =  
  ...  

myFunction (new sub :> super)
Run Code Online (Sandbox Code Playgroud)

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

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

myFunction new sub
Run Code Online (Sandbox Code Playgroud)