[英]What are row types? Are they algebraic data types?
我經常聽說F#缺乏對OCaml行類型的支持,這使得語言比F#更強大。
這些是什么? 它們是代數數據類型,例如總和類型(有區別的聯合)還是產品類型(元組,記錄)? 是否可以在其他方言中寫行類型,例如F#?
首先,我們需要修復術語。 至少在類型理論中,特別是在OCaml的類型系統中,沒有“行類型” †這樣的東西。 存在“行多態”,我們將在0以下討論它。
行多態性是多態的一種形式。 OCaml提供兩種多態 - 參數和行,而缺少其他兩種 - ad hoc和包含(也稱為子類型) 1 。
首先,什么是多態 ? 在類型系統的上下文中,多態性允許單個術語具有多種類型。 這里的問題是單詞類型本身在計算機科學和編程語言社區中嚴重超載。 因此,為了最大限度地減少混淆,讓我們在這里重新介紹它,在同一頁2上 。 一種術語通常表示術語語義的某種近似。 語義可以像一組配有一組操作或更復雜的值(如效果,注釋和任意理論)一樣簡單。 通常,語義表示一個術語的所有可能行為的集合。 類型系統‡表示一組規則,允許某些語言構造,並根據其類型禁止其他語言。 即,它驗證術語的組合行為是否正確。 例如,如果某種語言中存在函數應用程序構造,則類型系統將僅允許應用程序使用具有與參數類型匹配的類型的參數。 這就是多態性發揮作用的地方。 在單形類型系統中,這種匹配可能只是一對一,即文字。 多態類型系統提供了指定一些與一族類型匹配的正則表達式的機制。 因此,不同類型的多態性只是不同類型的正則表達式,您可以使用它們來表示類型族。
現在讓我們從這個角度來看待不同類型的多態。 例如,參數多態就像正則表達式中的點。 例如, 'a list
是. list
. list
- 表示我們按字面順序與list
匹配, list
類型的參數可以是任何類型。 行多態性是一個星型運算符,例如<quacks : unit; ..>
<quacks : unit; ..>
和<quacks : unit; .*>
<quacks : unit; .*>
。 這意味着它與任何類型的quacks
和其他任何類型3相匹配。 說到名義上的子類型,在這種情況下,我們有名義類(在regexp中也稱為字符類),並且我們指定了一系列具有其基類名稱的類型。 例如, duck
就像[:duck:]*
,任何正確注冊為類成員的值都與此類型匹配(通過類繼承和new運算符) 4 。 最后,ad-hoc多態實際上也是名義上的,並映射到正則表達式中的字符類。 這里的主要區別在於ad-hoc多態的類型概念不是應用於值,而是應用於名稱。 因此,名稱(如函數名稱或+
運算符)可能具有多個定義(實現),這些定義應該使用某種語言機制靜態注冊(例如,重載運算符,實現方法等)。 因此,ad-hoc多態只是名義子類型的特例。
現在,當我們清楚時,我們可以討論行多態性給我們帶來什么。 行多態性是結構類型系統的一個特征(也稱為動態類型語言中的鴨子類型),與提供子類型多態性的名義類型系統形成對比。 一般來說,正如我們上面所討論的,它允許我們指定一種類型為“任何嘎嘎叫”而不是“任何實現IDuck接口的東西”。 當然,你可以通過定義duck接口並使用一些inherit
或implements
機制顯式地將所有實現注冊為此接口的實例來對標稱類型執行相同的操作。 但這里的主要問題是您的層次結構是密封的,即您需要更改代碼以在新創建的接口中注冊實現。 這打破了開放/封閉原則並阻礙了代碼重用。 名義子類型的另一個問題是,除非你的層次結構形成一個格子(即,對於任何兩個類總是有一個最小上限),你不能在它上面實現類型推斷5 。
目標ML:ML的有效面向對象擴展 - 對該主題的全面描述。
FrançoisPottier和DidierRémy。 ML類型推理的本質。 在Benjamin C. Pierce,編輯,類型和編程語言高級主題,麻省理工學院出版社,2005年。 - 有關行的詳細解釋,請參見第10.8節。
結構多態性的簡單類型推斷 - 詳細解釋在存在類型推斷時結構和行多態之間的相互作用。
0)正如@nekketsuuu在評論中指出的那樣,我使用的術語有點自願,因為我的目的是提供一個易於理解和高層次的想法,而不是深入細節。 從那以后我修改了帖子,使其更加嚴格。
1)然而,OCaml提供了具有繼承和子類型概念的類,它仍然不是根據通用定義的子類型多態性,因為它不是名義上的。 從答案的其余部分可以更清楚地看出來。
2)我只是在修理術語,我並不是說我的定義是正確的。 許多人認為類型表示值的表示,並且歷史上這是正確的。
3)也許更好的正則表達式是<.*; quacks : unit; .*>
<.*; quacks : unit; .*>
<.*; quacks : unit; .*>
但我認為你明白了。
4)因此OCaml沒有子類型多態性,盡管它有一個子類型的概念。 當您指定一個與子類型不匹配的類型時,它只會按字面意思匹配,並且您需要使用顯式向上轉換運算符來使類型T的值適用於需要super(T)
的上下文中。 因此,盡管OCaml中存在子類型,但它與多態性無關。
5)雖然格子要求看起來不可能,但在現實生活中很難對層次結構施加這種限制,或者如果強加,則類型推斷的精度將始終與類型層次結構的精度相關聯。 所以在實踐中,它不起作用,參見 斯卡拉
† (在第一次閱讀時跳過此注釋)雖然在OCaml中存在行變量 ,用於將行多態嵌入到僅具有參數多態的OCaml類型推斷中。
‡)通常,單詞輸入與類型系統可互換使用,以指代整個類型系統中的一組特定規則。 例如,有時我們說“OCaml有行鍵入”來表示OCaml類型系統為“行多態”提供規則的事實。
行類型很奇怪。 而且非常強大。
行類型用於在OCaml中實現對象和多態變體。
但首先,這是沒有行類型我們不能做的事情:
type t1 = { a : int; b : string; }
type t2 = { a : int; c : bool; }
let print_a x = print_int x.a
let ab = { a = 42; b = "foo"; }
let ac = { a = 123; c = false; }
let () =
print_a ab;
print_a ac
這段代碼當然會拒絕編譯,因為print_a
必須有一個唯一的類型: t1
或t2
,但不是兩者。 但是,在某些情況下,我們可能需要這種確切的行為。 這就是行類型的用途。 這就是他們所做的:更“靈活”的類型。
在OCaml中,行類型有兩個主要用途: 對象和多態變體 。 在代數方面,對象為您提供“行產品”和多態變體“行和”。
關於行類型的注意事項是,您最終可以使用一些子類型進行聲明,並且非常反直觀的類型和語義(特別是在案例類中)。
您可以查看本文以獲取更多詳細信息。
我將用他的例子完成PatJ的優秀答案 ,用類編寫。
鑒於以下類別:
class t1 = object
method a = 42
method b = "Hello world"
end
class t2 = object
method a = 1337
method b = false
end
以下對象:
let o1 = new t1
let o2 = new t2
您可以編寫以下內容:
let print_a t = print_int t#a;;
val print_a : < a : int; .. > -> unit = <fun>
print_a o1;;
42
- : unit = ()
print_a o2;;
1337
- : unit = ()
您可以在print_a
的簽名中看到行類型。 < a : int; .. >
< a : int; .. >
是一種字面意思是“任何至少a
帶有簽名int
的方法a
對象”的類型 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.