[英]Why does OCaml sometimes require eta expansion?
如果我有以下OCaml功能:
let myFun = CCVector.map ((+) 1);;
它在Utop中運行良好,Merlin不會將其標記為編譯錯誤。 但是,當我嘗試編譯它時,我收到以下錯誤:
錯誤:此表達式的類型, (int,'_ a)CCVector.t - >(int,'_ b)CCVector.t ,包含無法泛化的類型變量
如果我eta-expand它然后它編譯好:
let myFun foo = CCVector.map ((+) 1) foo;;
所以我想知道為什么它不能以eta-reduced形式編譯,以及為什么eta-reduced表單似乎在頂層(Utop)中工作但在編譯時不起作用?
哦,CCVector的文檔就在這里 。 '_a部分可以是`RO或`RW,具體取決於它是只讀還是可變。
你在這里得到的是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 *)
修復后,您無法更改類型,從而防止上面的崩潰。
允許此輸入延遲,直到編譯單元的輸入結束。 toplevel是一個永不結束的單元,因此你可以在會話的任何地方使用'_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,對於多態性和副作用的問題沒有完美的解決方案。 與其他解決方案一樣,值多態限制也有其自身的缺點:如果您想要具有多態值,則必須以非擴展方式進行定義:您必須eta-expand myFun
。 這有點糟糕但被認為是可以接受的。
你可以閱讀其他一些答案:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.