2

問題爲什麼編譯器將不同名稱的泛型類型變量的兩個等價簽名標識爲不同的類型?

爲什麼

val of_bindings : (key * '_a) list -> '_a t 
val of_bindings : (key * 'a) list -> 'a t 

不同的簽名?

語境

我有一些地圖擴展實現:

MAPEXT.ml:

module type T = sig 
    include Map.S  
    val of_bindings : (key * 'a) list -> 'a t 
    end 

mapExt.mli:

module Make (Key : Map.OrderedType) 
    : MAPEXT.T with type key = Key.t 

mapExt.ml:

module Make (Key : Map.OrderedType) = struct 
    include Map.Make (Key) 
    let of_bindings = 
     let rec of_bindings acc = 
     function | (k, v) :: t -> of_bindings (add k v acc) t 
       | []   -> acc in 
     of_bindings empty 
    end 

編譯器給了我一個錯誤的 ocamlopt -c MAPEXT.ml mapExt.mli mapExt.ml

Error: The implementation mapExt.ml does not match the interface mapExt.cmi: ... At position module Make(Key) : Values do not match: val of_bindings : (key * '_a) list -> '_a t is not included in val of_bindings : (key * 'a) list -> 'a t File "mapExt.ml"

結果我以爲泛型類型變量的名稱並不重要,只是信號不同類型。但從我現在看到的他們似乎有不同的含義。

如何避免這個問題來編譯這段代碼?

相關:What is the difference between 'a and '_l?

+1

請閱讀「通過部分應用程序獲得的函數不夠多態」以及https://ocaml.org/learn/faq.html#Typing – camlspotter

回答

3

這不只是一個不同的名稱(你是對一個類型變量的名稱並不重要)。如果一個類型變量名以'_序列開頭,那麼這是一種編譯器方式來說這個類型變量是弱多態的。簡而言之,它根本不是多形的,它表示你的價值不是通用的。基本上,類型變量表示您的值具有多種類型,例如,'a list是可能屬於一系列類型的值,例如int list,string list等。換句話說,類型變量表示無限範圍類型,即它是所有符號。弱類型變量是相反的,因爲它不包含多種類型,但僅限於一種類型,即如果您有類型爲'_a list的值,則表示存在類型爲x(只有一個),例如您的值的類型是x list。只是編譯器還不知道類型。出於禮貌的考慮,編譯器給了我們一個額外的緯度,並且不給出類型錯誤。

爲什麼一個類型變量沒有被推廣到所有的概念,而是堅持存在的表示法,隱藏了OCaml和函數應用程序的可變性。一般規則是,如果編譯器不能證明值計算沒有任何可觀察到的副作用,那麼該值不是一般化的,並且所有類型變量都很弱。由於從部分函數應用程序中獲得的值是任意計算的結果,因此編譯器保守地認爲計算可能有副作用,並且不能概括類型。這被稱爲value restriction,它是OCaml類型系統的一個特徵。處理它的通常方法是添加所有參數,以便值不是由部分應用程序生成的,而是將成爲語法值 - 在編譯期間計算(確定)的一類值。該機制的花式名稱是Eta Expansion

相關問題