如何使用 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)。子类型化为您提供了多态性——一种在不同实现上使用相同抽象的能力。