如何使用 OCaml 中的 class 类型强制 class 中的 val 在 class 中不可变
How to force a val in a class to be immutable in class using class type in OCaml
假设我有这个:
class type point_t =
object
val x : int
method getx : int
method move : int -> unit
end;;
我可以像这样写一个 class,它会起作用:
class point : point_t =
object
val mutable x = 0
method getx = x
method move d = x <- x + d
end;;
现在假设我想创建一个 class 类型,它不允许 class 定义为可变 val x(我希望 x 是不可变的)。有办法吗?
这是不可能的,所以如果您不想让实现使用可变变量,最好将其全部隐藏并公开功能 getter/setter:
class type point_t = object(self)
method get_x : int
method with_x : int -> self
method move : int -> self
end;;
如果您只想允许通过 move
方法进行更新,您可以省略 with_x
方法。
这样做的原因是,具有可变版本变量的 class 是具有相同变量不可变版本的 class 的适当子 class,因为它具有相同的一组操作,再加上一个 - 设置变量的能力。因此,任何对类型 point_t
的抽象都可以应用于具有或不具有可变性的 class 实例(尽管它不能改变变量)。请注意,相反的情况是不可能的,如果您将使用可变 x
定义 class 类型 point_t
,并尝试使用不可变类型来实现它,那么类型系统将抱怨。由于您的实施不提供所有操作。
此外,您可能会错过一件事。尽管 class point
有一个可变变量 x
,但这种可变性实际上被类型约束 point_t
密封(即隐藏)。所以,无论实现是什么,接口都被严格定义为具有不可变的 x
:
class the_point = object
inherit point
method! move d = x <- x - d
end
method! move d = x <- x - d
^^^^^^^^^^
Error: The instance variable x is not mutable
您可能会感到困惑,因为您对 OOP 的 Java/C++ 风格有一些经验,其中 class 类型是名义上的,并且 class 可以成为子另一个 class 的 class 只能通过显式继承。在 OCaml 中,如果 class 是另一个 class 的 语法 超集,即它至少具有所有超级领域 class。没有必要继承一个超级class,成为它的子class。 class point : point_t
不是继承,而是类型约束,它说:这里是 class 表达式,它实现了 point_t
class (也许更多),拜托,make确保它是真实的,只向外界公开 point_t
接口。
最后一点,我特别指出术语 sub classing 作为 syntactic 超集super class 强调继承和 sub classing 并不意味着子类型化。后者是语义(即实例的行为),前者是语法,即一组代码片段。 Subclassing 为您提供了代码重用,能够从 superclasses 复制代码(因为 inherit
实际上只是 copy/pasting super [=46= 的代码] 给你的子 class)。子类型化为您提供了多态性——一种在不同实现上使用相同抽象的能力。
假设我有这个:
class type point_t =
object
val x : int
method getx : int
method move : int -> unit
end;;
我可以像这样写一个 class,它会起作用:
class point : point_t =
object
val mutable x = 0
method getx = x
method move d = x <- x + d
end;;
现在假设我想创建一个 class 类型,它不允许 class 定义为可变 val x(我希望 x 是不可变的)。有办法吗?
这是不可能的,所以如果您不想让实现使用可变变量,最好将其全部隐藏并公开功能 getter/setter:
class type point_t = object(self)
method get_x : int
method with_x : int -> self
method move : int -> self
end;;
如果您只想允许通过 move
方法进行更新,您可以省略 with_x
方法。
这样做的原因是,具有可变版本变量的 class 是具有相同变量不可变版本的 class 的适当子 class,因为它具有相同的一组操作,再加上一个 - 设置变量的能力。因此,任何对类型 point_t
的抽象都可以应用于具有或不具有可变性的 class 实例(尽管它不能改变变量)。请注意,相反的情况是不可能的,如果您将使用可变 x
定义 class 类型 point_t
,并尝试使用不可变类型来实现它,那么类型系统将抱怨。由于您的实施不提供所有操作。
此外,您可能会错过一件事。尽管 class point
有一个可变变量 x
,但这种可变性实际上被类型约束 point_t
密封(即隐藏)。所以,无论实现是什么,接口都被严格定义为具有不可变的 x
:
class the_point = object
inherit point
method! move d = x <- x - d
end
method! move d = x <- x - d
^^^^^^^^^^
Error: The instance variable x is not mutable
您可能会感到困惑,因为您对 OOP 的 Java/C++ 风格有一些经验,其中 class 类型是名义上的,并且 class 可以成为子另一个 class 的 class 只能通过显式继承。在 OCaml 中,如果 class 是另一个 class 的 语法 超集,即它至少具有所有超级领域 class。没有必要继承一个超级class,成为它的子class。 class point : point_t
不是继承,而是类型约束,它说:这里是 class 表达式,它实现了 point_t
class (也许更多),拜托,make确保它是真实的,只向外界公开 point_t
接口。
最后一点,我特别指出术语 sub classing 作为 syntactic 超集super class 强调继承和 sub classing 并不意味着子类型化。后者是语义(即实例的行为),前者是语法,即一组代码片段。 Subclassing 为您提供了代码重用,能够从 superclasses 复制代码(因为 inherit
实际上只是 copy/pasting super [=46= 的代码] 给你的子 class)。子类型化为您提供了多态性——一种在不同实现上使用相同抽象的能力。