簡體   English   中英

在 Haskell 中,為什么存在類型類層次結構/繼承?

[英]In Haskell, why is there a typeclass hierarchy/inheritance?

為了澄清我的問題,讓我以或多或少等效的方式重新表述它:

為什么Haskell中有超類/類inheritance的概念? 導致這種設計選擇的歷史原因是什么? 例如,如果有一個沒有 class 層次結構的基礎庫,只有相互獨立的類型類,為什么會如此糟糕?

在這里,我將揭露一些讓我想問這個問題的隨機想法。 我目前的直覺可能不准確,因為它們是基於我目前對 Haskell 的理解,這並不完美,但它們在這里......

我不清楚為什么 Haskell 中存在 class inheritance 類型。 我覺得這有點奇怪,因為它在概念上造成了不對稱。 通常在數學中,可以從不同的角度定義概念,我不一定要支持應該如何定義它們的順序。 好的,有一些證明事情的順序,但是一旦定理和結構存在,我寧願將它們視為可用的獨立工具。

此外,我在 class inheritance 中看到的一件可能不太好的事情是這樣的:我認為 class 實例會默默地選擇一個相應的超類實例,這可能是最自然的那個類型。 讓我們考慮一個被視為 Functor 子類的 Monad。 也許有不止一種方法可以在某種類型上定義 Functor,而這種類型也恰好是 Monad。 但是說一個 Monad 是一個 Functor 隱含地為那個 Monad 選擇了一個特定的 Functor。 有一天,你可能會忘記實際上你想要一些其他的 Functor。 也許這個例子不是最合適的,但我覺得如果你的 class 是許多孩子的孩子,這種情況可能會普遍化並且可能很危險。 當前的 Haskell inheritance 聽起來像是隱式地對父母進行了默認選擇。

相反,如果你有一個沒有層次結構的設計,我覺得你總是必須明確所有所需的屬性,這可能意味着風險更小、更清晰、更對稱。 到目前為止,我所看到的是這種設計的成本將是:對於從一組概念到另一組概念的每次有意義的轉換,在實例定義和新類型包裝器中編寫更多約束。 我不確定,但也許這是可以接受的。 不幸的是,我認為新類型的 Haskell 自動派生機制不能很好地工作,我希望該語言在新類型包裝/展開方面更智能,並且需要更少的冗長。 我不確定,但現在我考慮一下,也許新類型包裝器的替代方案可能是包含特定實例變體的模塊的特定導入。

我在寫這篇文章時想到的另一種選擇是,也許可以削弱 class ( P class (P x) => C x的含義,而不是要求C的實例,我們可以選擇實例 PZ1它松散地表示例如C class 也包含P的方法,但沒有自動選擇P的實例,與P不存在其他關系。 所以我們可以保留某種可能更靈活的較弱層次結構。

如果您對該主題有一些澄清,和/或糾正我可能的誤解,謝謝。

也許你已經厭倦了聽到我的消息,但是這里...

我認為超類是作為類型類的一個相對次要和不重要的特性引入的。 Wadler 和 Blott, 1988中,它們在第 6 節中進行了簡要討論,其中給出了示例class Eq a => Num a 在那里,提供的唯一理由是必須在 function 類型中寫入(Eq a, Num a) =>...很煩人,而應該“明顯”可以添加、乘法和求反的數據類型應該也可以測試是否相等。 超類關系允許“方便的縮寫”。

(這個特性的不重要性被這個例子如此糟糕的事實所強調。現代 Haskell 沒有class Eq a => Num a因為所有Num的邏輯理由也是Eq s 太弱了。例子class Eq a => Ord a會更有說服力。)

因此,在沒有任何超類的情況下實現的基礎庫看起來或多或少是相同的。 在庫和用戶代碼中對 function 類型簽名只會有更多邏輯上多余的約束,而不是回答這個問題,我會回答一個初學者問題,為什么:

leq :: (Ord a) => a -> a -> Bool
leq x y = x < y || x == y

不輸入檢查。

關於強制特定層次結構的超類的觀點,您錯過了目標。

這種“強制”實際上是類型類的一個基本特征。 類型類是“由設計決定的”,並且在給定的 Haskell 程序中(其中“程序”包括所有庫,包括程序使用的base ),對於特定類型,特定類型 class 只能有一個實例。 這種性質稱為連貫性。 (即使有一個語言擴展IncohorentInstances ,它也被認為是非常危險的,並且只應在特定類型的所有可能實例 class 的所有可能實例在功能上等效時使用。)

這種設計決策伴隨着一定的成本,但也帶來了許多好處。 Edward Kmett 在此視頻中詳細討論了這一點,從大約 14:25 開始。 特別是,他將 Haskell 的設計一致性類型類與 Scala 的非一致性設計隱式進行比較,並將 Scala 方法帶來的增強功能與“啞數據類型”的可重用性(和重構優勢)進行對比Haskell 方法。

因此,設計空間中有足夠的空間用於連貫的類型類和不連貫的隱式,而 Haskell 的方法不一定是正確的。

但是,由於 Haskell 選擇了一致的類型類,因此擁有特定的層次結構沒有“成本”:

class Functor a => Monad a

因為,對於特定類型,例如[]MyNewMonadDataType ,無論如何只能有一個Monad和一個Functor實例。 超類關系引入了一個要求,即任何具有Monad實例的類型都必須具有Functor實例,但它並不限制Functor實例的選擇,因為你一開始就沒有選擇 或者更確切地說,您的選擇是在具有零個Functor []實例和恰好一個之間。

請注意,這與Monad類型是否只有一個合理的Functor實例的問題是分開的。 原則上,我們可以使用不兼容的FunctorMonad實例來定義違反法律的數據類型。 我們仍然被限制在整個程序中使用那個Functor MyType實例和那個Monad MyType實例,無論Functor是否是Monad的超類。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM