4

我在理解不同類型的多態性時遇到問題,特別是關於OCaml。我明白,多態性允許OCaml中的多種類型表示爲'a,但我不明白不同類型的多態性是什麼。
如果有人可以給我一個相對低級的語言解釋,那就太棒了! 特設,參數,包含/子類型OCaml中的多態性 - 特設,參數化,包含/子類型

回答

0

我其實並不認爲這種問題特別適合堆棧溢出的優點。有關於類型的完整書籍。事實上,我建議讀皮爾斯的Types and Programming Languages,我發現它非常有啓發性和令人愉快。

作爲一個快速回答(主要基於我從皮爾斯記得的:-),這裏是我對這些術語的看法。

參數多態性是指具有自由變量的類型,其中變量可以被任何類型替換。函數List.length具有這樣的類型,因爲它可以找到任何列表的長度(不管元素的類型如何)。

# List.length;; 
- : 'a list -> int = <fun> 

一個約OCaml中的美妙的事情是,它不僅支持類型,像這種情況,推斷他們。給定一個函數定義,OCaml爲函數推斷最一般的參數多態類型。

子類型是類型之間的關係。 A型Ť是類型ü的一個亞型如果T的所有實例都還ü實例(但不一定反之亦然)。 OCaml支持子類型,也就是說,它允許程序將T的值作爲其超類型的值。但是,程序員必須明確地詢問這一點。

# type ab = [ `A | `B ];; 
type ab = [ `A | `B ] 
# type abc = [`A | `B | `C ];; 
type abc = [ `A | `B | `C ] 
# let x : ab = `A;; 
val x : ab = `A 
# let y : abc = x;; 
Error: This expression has type ab but an expression was expected 
of type abc. The first variant type does not allow tag(s) `C 
# let y : abc = (x :> abc);; 
val y : abc = `A 

在這個例子中,類型式ababc類型的子類型,和x具有類型ab。可以使用x作爲abc類型的值,但必須使用:>類型運算符進行顯式轉換。

Ad-hoc多態性是指程序員爲特定情況定義的多態性,而不是從基本原理派生的。 (或者至少這就是我的意思,也許其他人會用不同的術語。)一個可能的例子是OO繼承層次結構,其中對象狀態的實際類型不需要以任何方式關聯,只要方法有適當的關係。

ad-hoc多態性(IMHO)的關鍵觀察在於程序員是否能夠正常工作。因此,它並不總是工作。這裏基於基本原理的其他類型的多態性實際上不能失效。在處理複雜系統時,這是一種令人欣慰的感覺。

+0

當然,你的意思是特設多態,不是即席*亞型*? – antron

+0

謝謝,我修好了。 –

+0

但ad-hoc多態不是這個。本例中描述的是一個對象系統,其中對象的佈局不參與評估子類型關係(即一種類型是否是另一種類型的子類型)。特設多態不是子類型的一種形式。 – antron

11

下面是一個近似值。

即席多態通常指能夠用不同類型聲明相同的名稱(通常是函數),例如, SML中的+ : int -> int -> int+ : float -> float -> float。這些功能是不同的,它們可以以完全不同的方式工作,但編譯器或解釋器根據上下文選擇適當的函數。我無法想象OCaml中的任何ad-hoc多態性。然而,這在C++和Java中很常見。

參數多態性是指單個函數可以與任何類型的參數一起工作,因爲沒有試圖查看該參數的結構。例如,cons : 'a -> 'a list -> 'a list可以將任何類型的值v預加到相同類型的值列表中,因爲consv的結構(佈局)是什麼或它支持哪些操作並不重要。在C語言中,cons不需要「取消引用」指針,或者對v執行任何特定於v的實際類型的操作。請注意,與ad-hoc多態性不同,cons對所有類型的行爲都是相同的。因此,參數和特定多態性是彼此天然「相反」的一種方式。參數多態性是OCaml中絕大多數多態性實例的原因。

亞型多態性是當您可以使用t類型的值時,預期類型爲u的值。這可能是因爲t類型支持u類型的所有操作,或者因爲t的結構可用於需要u的地方。這樣做的例子可以是子類別(也許總線可以用於車輛的任何地方)或多態變體(您可以使用'A | 'B,其中'A | 'B | 'C預計)。根據註釋

編輯,然而,子類型必須OCaml中明確要求。例如,如果您有一個功能f : u -> int,並且您想將其應用於v : t,其中tu的子類型,則必須編寫f (v :> u)(v :> u)語法是一種類型的強制。

OCaml還支持行多態,這是一種帶約束的參數多態的形式。如果f改爲f : #u -> int(用於對象類型)或f : [< u] -> int(用於多態變體),則語法代表類型變量,類似於'a,但其只能用各自的「子類型」u(在限制意識到他們可以分別支持更多的字段/更少的構造函數)。然後,你可以做f v沒有脅迫。 OCaml自動推斷對包含多態變量和對象的多個表達式使用行多態的類型,但是如果要創建簽名,則必須明確地寫出類型。

行多態性有更多的用法和注意事項。我忽略了實際的行變量和附加語法,只描述了看起來像有限量化的東西(如Java泛型)。關於行多態性,其名稱和/或其形式主義的更詳細和準確的討論,可能是最好的保存單獨的問題。

+0

OCaml中的多態變體(和對象)不是*子類型,而是基於參數多態的行多態。子類型僅在添加類型轉換(在OCaml中顯式)時纔會出現。其餘絕對正確。此外,adhoc多態的一個很好的例子是Haskell中的類型(和模塊蘊涵...)。 – Drup

+0

大多數都是正確的 - 但是你必須明確地寫出用於子類型的強制轉換纔是OCaml的細節。我不想讓概念答案複雜化。它*是真實的,多態總和和對象是支持子類型關係的類型。也有涉及這些類型的行多態性,並且它覆蓋了一些子類型的使用情況,而不需要OCaml中的明確強制,這是另一回事。並且,行多態性當然不是多態變體和對象「所」 - 只是他們支持的一件事。我會在一張紙條上工作。 – antron

0

IDENT是多形性:

# let ident x = x;; 
val ident : 'a -> 'a = <fun> 

# ident 1;; 
- : int = 1 
# ident "ok";; 
- : string = "ok" 
# ident [];; 
- : 'a list = [] 
# ident List.length;; 
- : '_a list -> int = <fun> 
# ident ident;; 
- : '_a -> '_a = <fun> 

倍也:

# open List;; 
# fold_left (+) 0 [1;2;3];; 
- : int = 6 
# fold_left (^) "" ["1";"2";"3"];; 
- : string = "123" 
# fold_left (fun a (x,y) -> a+x*y) 0 [(1,2);(3,4);(5,6)];; 
- : int = 44