2013-03-09 77 views
14

我正在瀏覽ocaml的標準庫,並在map.ml文件中遇到此代碼。爲什麼在這種類型之前有一個加號?

module type S = 
    sig 
    type key 
    type +'a t 
    val empty: 'a t' 

我不知道爲什麼會有type +'a t,爲什麼筆者使用它,而不是簡單地'a t
它的行爲很奇怪,我不能推斷它的用法。

# type +'a t = 'a list;; 
type 'a t = 'a list 
# type +'a t = +'a list;; 
Characters 13-14: 
    type +'a t = +'a list;; 
      ^
Error: Syntax error 

由於

+0

Jane Street的相關文章:https://blogs.janestreet.com/a-and-a/ – 2017-03-17 14:05:10

回答

14

要在傑弗裏的答案建立,開發商在這裏做標記抽象類型作爲協工作的原因可能是幫助您使用子類型(基本沒人用的子類型OCaml中,爲參數polymorphis一般是優選),但是要使用稱爲「寬鬆值限制」的類型系統中一個不那麼廣爲人知的方面,這要歸功於協變抽象類型允許更多的多態性。

您可能會安全地忽略這些細微之處,直到有一天您遇到了一個與您的抽象類型不一樣多態的問題,然後您應該記住,簽名中的協方差註釋可能會有所幫助。

我們在幾個月前討論過這個on reddit/ocaml

考慮下面的代碼示例:代替'a C.collection,你會期望

module type S = sig 
    type 'a collection 
    val empty : unit -> 'a collection 
end 

module C : S = struct 
    type 'a collection = 
    | Nil 
    | Cons of 'a * 'a collection 
    let empty() = Nil 
end 

let test = C.empty() 

你得到test的類型是'_a C.collection。它不是一個多態類型('_a是一個單態推理變量,尚未完全確定),並且在大多數情況下您不會滿意它。

這是因爲C.empty()不是一個值,所以它的類型不是泛化的(〜made polymorphic)。從鬆弛值限制中獲益,你必須標記抽象'a collection類型協變:

module type S = sig 
    type +'a collection 
    val empty : unit -> 'a collection 
end 

當然是因爲該模塊C與簽名S密封這只是發生:module C : S = ...。如果模塊C沒有給出明確的簽名,那麼類型系統會推斷出最一般的方差(這裏是協方差),並且人們不會注意到這一點。對定義一個函子,或者強制執行一個虛幻類型的規則,或者編寫模塊化程序時,編程對一個抽象接口通常是有用的,所以這種情況肯定會發生,因此知道寬鬆的值限制是很有用的。

如果你想了解的理論,價值約束與放鬆從雅克常綠矮灌木叢,它的前幾頁都是一個相當有趣和方便的介紹主題和中心思想2004年的研究文章Relaxing the value restriction進行了討論。

+0

感謝您的詳細解釋。我回到了map.ml,發現以下行:type'a t = Empty | 'a t * key *'a *'a t * int的節點非常類似於您的示例。但是,你能解釋更多關於單形推理類型嗎?看起來'_c C.collection仍然匹配'C.collection。如果C.empty()的類型不是一般化的,那麼什麼時候會導致什麼問題呢? – octref 2013-03-09 15:46:38

+2

@octref:嘗試使用函數'let id =(fun x - > x)(fun x - > x)',它的類型爲'_a - >'_a'(因爲它的定義是一個應用程序而不是值,其類型變量出現在正面和負面的位置)。你會很快看到問題:你不能在兩種不同的類型中使用它,所以它不是多態的。 – gasche 2013-03-09 16:43:31

+0

感謝gasche,這真的很好知道。我想像它只是關於普通的舊分類(我幾乎從不使用,現在你提到它)。 – 2013-03-09 16:44:52

12

這標誌着相對於所述模塊的類型的類型作爲協變。假設您有兩個鍵的鍵是相同類型的映射。這+說,如果一個地圖A的值是另一個地圖B的值的子類型,那麼地圖A的整體類型是地圖B的類型的子類型。我在Jane Street blog中發現了相當不錯的描述。

+0

如果地圖A是地圖B的類型的子類型,那麼這是否意味着我將能夠使用B對A?它是否類似於Java中的子類概念? – octref 2013-03-09 16:03:13

+1

OCaml的非OO部分根本不像Java。 Java子類化*是一種子類型,但是(我相信這裏的子類化)子類化的使用在OCaml中很少見,而子類化是Java的一個關鍵特性。請注意,OCaml不會推斷子類型。您需要將表達式的類型明確強制爲超類型。這很好,因爲它幾乎從未出現。但是,當你這樣做時,是的,它允許爲超類型定義的函數(不僅僅是方法,所有函數)被應用於子類型。 – 2013-03-09 16:39:02

+0

感謝您的解釋! – octref 2013-03-10 14:17:12