2014-09-10 47 views
19

如果我有以下OCaml的功能:OCaml爲什麼有時需要eta擴展?

let myFun = CCVector.map ((+) 1);; 

它工作正常,在UTOP,梅林並沒有將其標記爲一個編譯錯誤。當我嘗試編譯它,但是,我得到以下錯誤:

錯誤:此類型的表達, (INT, '_a)CCVector.t - >(INT,' _b)CCVector.t, 包含鍵入不能一概而論

如果我ETA-擴大它的變量然而然後它編譯罰款:

let myFun foo = CCVector.map ((+) 1) foo;; 

所以我想知道爲什麼它不ETA還原形式編譯,以及爲什麼eta縮減形式似乎w ork在頂層(Utop)而不是在編譯時?

哦,CCVector的文檔是here。 '_a部分可以是'RO'或'RW',具體取決於它是隻讀還是可變。

+0

搜索我注意到你已經把'haskell'標籤上這個。雖然哈斯克爾有「可怕的單態限制」,一見鍾情的行爲與Ocaml的價值限制相似,但兩種限制的原因是非常不同的。 Ocaml使用它來馴服副作用,但Haskell是純粹的,所以不需要它。 Haskell的限制可以防止對值的意外重新評估,並且它不是必須的 - 對於某些類型的代碼,它比煩人更有趣,並且有一個關閉它的流行選項。 – 2014-09-10 15:05:24

+2

它們比你想象的更加類似。 – augustss 2014-09-11 00:34:54

回答

23

你在這裏得到的是ML語言族的價值多態性限制。

限制的目的是解決let-polymorphism和副作用。例如,在下面的定義:

let r = ref None 

r不能有一個多態型'a option ref。否則:

let() = 
    r := Some 1;  (* use r as int option ref *) 
    match !r with 
    | Some s -> print_string s (* this time, use r as a different type, string option ref *) 
    | None ->() 

錯誤類型檢查是有效的,但它崩潰,由於參考單元r用於這兩個不兼容的類型。

爲了解決這個問題,許多研究都是在80年代完成的,多價值主義的價值就是其中之一。它限制多態只能讓定義形式爲「非擴展性」的綁定。 Eta擴展形式是非擴展性的,因此您的eta擴展版本myFun具有多態類型,但不會爲eta減少一個。 (更準確地說,OCaml使用了這個值多態性的輕鬆版本,但故事基本上是相同的。)

當let綁定的定義是可擴展的時,沒有引入多態性,因此類型變量保持非泛化。這些類型在頂層打印爲'_a,他們的直觀含義是:他們必須被實例化到一些具體的類型後:

# let r = ref None       (* expansive *) 
val r : '_a option ref = {contents = None} (* no polymorphism is allowed *) 
              (* type checker does not reject this, 
               hoping '_a is instantiated later. *) 

我們可以定義後修復型'_a

# r := Some 1;;        (* fixing '_a to int *) 
- : unit =() 
# r;; 
- : int option ref = {contents = Some 1}  (* Now '_a is unified with int *) 

一旦修復,您將無法更改類型,從而防止上面的崩潰。

這種鍵入延遲被允許直到編譯單元的鍵入結束。頂層是一個永不結束的單元,因此您可以在會話的任何位置使用'_a類型變量的值。但在分離編譯,'_a變量必須被實例化某種類型的無類型變量,直到ml文件的末尾:

(* test.ml *) 
let r = ref None (* r : '_a option ref *) 
(* end of test.ml. Typing fails due to the non generalizable type variable remains. *) 

這又有什麼用編譯器的myFun功能發生。

AFAIK,沒有完美的解決多態性和副作用的問題。與其他解決方案一樣,值多態性限制也有其自身的缺點:如果您想要具有多態值,則必須使該定義不擴展:您必須擴展myFun。這有點糟糕,但被認爲是可以接受的。

你可以閱讀一些其他答案:

相關問題