在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编程的一个非常常见的用例。推荐的方法是什么?
答案 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确实通过两种不同的结构来支持它。
第一种是类型强制。如果super
是sub
的超类型(意味着在语义上,类型sub
的值表现为super
)和x : sub
的值,那么{{1 }}。 (x :> super) : super
类型运算符使转换显式化 - 相当于当您使用:>
类型的avalue时,流行的面向对象语言会隐式执行此操作。
第二个是超类型约束:要求给定的类型变量是给定类型的子类型。如果您希望在其中命名类型变量,则将其写为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
:即使您通过了两个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;