2016-07-05 13 views
2

使用類類型是在類不變比方說,我有這樣的:如何強制VAL一類OCaml中

class type point_t = 
    object 
     val x : int 
     method getx : int 
     method move : int -> unit 
    end;; 

我可以寫一個類像這樣,它會工作:

class point : point_t = 
    object 
     val mutable x = 0 
     method getx = x 
     method move d = x <- x + d 
    end;; 

現在假設我想創建一個類類型,它不允許用一個可變的val x(我想x是不可變的)定義一個類。有沒有辦法做到這一點 ?

回答

3

這是不可能的,所以如果你不希望允許實現使用可變變量它只是爲了更好地隱藏這一切,並揭露功能的getter/setter:

class type point_t = object(self) 
    method get_x : int 
    method with_x : int -> self 
    method move : int -> self 
end;; 

可以省略with_x方法,如果你只允許通過move方法更新。

對此的推理是,具有可變版本變量的類是具有相同變量的不可變版本的類的正確子類,因爲它具有相同的一組操作,再加上一個 - 能夠設置變量。因此,任何類型爲point_t的抽象都可以應用於具有或不具有可變性的類實例(儘管它不能改變該變量)。請注意,相反是不可能的,如果您將類型point_t定義爲可變x,並且會嘗試使用不可變的實現它,那麼類型系統將會抱怨。由於您的實施不提供所有操作。

此外,有一件事情,你可能會錯過。儘管類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++風格的一些經驗出現名義上,而一個類只能通過顯式繼承成爲另一個類的子類。在OCaml中,一個類是另一個類的子類,如果它是一個語法超集,即它是否至少包含超類的所有字段。沒有必要從超類繼承,成爲它的子類。並且class point : point_t不是繼承,而是一個類型約束,它表示:這裏是類表達式,它實現了point_t類(也許更多),請確保它是真實的,並且只向外界暴露接口point_t

而最後一點,我已經明確表示長期子分級作爲句法超集超類的強調一個事實,即繼承和子類化並不意味着亞型。後者是語義(即實例的行爲),前者是語法,即一組代碼片段。子類化爲您提供了代碼重用,從超類複製代碼的能力(因爲inherit實際上只是將超類的代碼複製/粘貼到您的子類)。子類型爲您提供了多態 - 可以在不同的實現上使用相同的抽象。

+0

我覺得奇怪的是,val_x和val_point_t之間的權限沒有什麼區別,但我想這可能不是真的需要它。幾周前剛剛開始學習OCaml。謝謝 ! –

+0

@ J-Lab有一個區別,我已經用這個行爲背後的推理更新了我的答案。希望它能幫助你。 – ivg