你在這裏得到的是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
。這有點糟糕,但被認爲是可以接受的。
你可以閱讀一些其他答案:
搜索我注意到你已經把'haskell'標籤上這個。雖然哈斯克爾有「可怕的單態限制」,一見鍾情的行爲與Ocaml的價值限制相似,但兩種限制的原因是非常不同的。 Ocaml使用它來馴服副作用,但Haskell是純粹的,所以不需要它。 Haskell的限制可以防止對值的意外重新評估,並且它不是必須的 - 對於某些類型的代碼,它比煩人更有趣,並且有一個關閉它的流行選項。 – 2014-09-10 15:05:24
它們比你想象的更加類似。 – augustss 2014-09-11 00:34:54