[英]Why type constructors can not be used in typeclass definition in haskell?
data LinkedList a = Empty | Cons a (LinkedList a) deriving (Eq, Show)
instance Foldable LinkedList where
foldMap _ Empty = mempty
foldMap f (a `Cons` ll) = LinkedList (f a) (foldMap f ll)
似乎 LinkedList 構造函數不在 scope 中? 為什么?
編譯器錯誤:
Data constructor not in scope: LinkedList :: m -> m -> m
|
17 | foldMap f (a `Cons` ll) = LinkedList (f a) (foldMap f ll)
| ^^^^^^^^^^
我搜索了一個解決方案,似乎我應該使用“映射”而不是構造函數。 這非常令人困惑。 我沒有在任何地方定義任何 Monoid 類型類。
你能解釋為什么mappened
而不是構造函數嗎? Monoid
函數mappened
和mempty
是在哪里定義的?
編輯 - - - - - - - - - - - - - - - - - - - - - - - - - --------------------
當我從 mappened 改回來時,我犯了一個錯誤。 對不起,但正確的問題是:
instance Foldable LinkedList where
foldMap _ Empty = mempty
foldMap f (a `Cons` ll) = Cons (f a) (foldMap f ll)
編譯器錯誤:
• Occurs check: cannot construct the infinite type:
m ~ LinkedList m
• In the second argument of ‘Cons’, namely ‘(foldMap f ll)’
|
17 | foldMap f (a `Cons` ll) = Cons (f a) (foldMap f ll)
|
^^^^^^^^^^^^
為什么我不能遞歸使用 foldMap? 以及 Monoid 函數的定義位置。
在
data LinkedList a = Empty | Cons a (LinkedList a)
你有
LinkedList
,它是一個類型構造函數:它將一般類型a
作為參數,並返回類型LinkedList a
;Cons
,它是一個值構造函數:它接受一個通用類型a
的值和一個LinkedList a
類型的值作為參數。另一方面,這里
foldMap f (a `Cons` ll) = LinkedList (f a) (foldMap f ll)
您使用LinkedList
就好像是一個值構造函數,這是錯誤的。
編譯器錯誤消息應該包含足夠的詳細信息,以便您找出代碼有什么問題:
• Occurs check: cannot construct the infinite type:
m ~ LinkedList m
• In the second argument of ‘Cons’, namely ‘(foldMap f ll)’
|
17 | foldMap f (a `Cons` ll) = Cons (f a) (foldMap f ll)
|
^^^^^^^^^^^^
忽略有關“發生檢查”和“無限類型”的部分,我接受這似乎有點令人困惑,它抱怨當foldMap f ll
具有實際類型LinkedList m
m
,它應該具有類型 m ,或者可能(事實證明) 反過來。 所以讓我們自己看一下類型。
它從foldMap
的類型開始,我們可以在文檔中輕松找到它:
foldMap :: Monoid m => (a -> m) -> t a -> m
這意味着foldMap f ll
將具有類型m
,對於任何 Monoid m
(調用者選擇的特定 Monoid 作為 function f
的返回類型)。
同時, f
的類型a -> m
,因此對於f
選擇的任意Monoid
實例, fa
必須再次具有m
- 類型。 因此,當fa
用作Cons
的第一個參數 - Cons (fa) (foldMap f ll)
- 我們可以比較Cons
的定義,它來自類型定義:
data LinkedList a = Empty | Cons a (LinkedList a)
這告訴我們,由於fa
具有m
類型,因此Cons (fa) (foldMap f ll)
必須被認為是LinkedList m
類型的值。 因此foldMap f ll
必須具有相同的類型。 (因為定義中的兩個LinkedList
類型都應用於同一個類型a
。)
這給了我們編譯器報告的問題 - foldMap f ll
必須同時屬於m
類型和LinkedList m
類型。
(旁白:GHC 在那一點上實際上並沒有放棄,它假設類型實際上是彼此相等的,並查看導致的結果。但在這里它只會導致無限回歸:相關值必須具有等於m
的類型LinkedList m
等於LinkedList (LinkedList m)
等等。Haskell 在值定義中可以像這樣無限回歸,但不允許它用於類型 - 因此“無法構造無限類型”。)
我們如何修復這個錯誤並獲得正確的foldMap
定義? 在計算foldMap f (Cons a ll)
時,您嘗試結合fa
和foldMap f ll
絕對是正確的想法。 這兩個都是m
類型,用於f
所針對的特定(任意)Monoid。 我們不一定能將它們與諸如(+)
之類的“正常函數”結合起來,因為我們不知道m
是什么類型 - 它不一定是數字類型或其他類型。 但是我們知道的一件事是它是Monoid的一個實例——因此我們確實可以將任意兩個這樣的值與Monoid
方法mappend
結合起來。 事實上,我們真的無能為力,因為我們對m
類型一無所知,除了它是Monoid
的一個實例 - 因此有mappend:: m -> m -> m
(和mempty:: m
)可用的。
所以只有一種方法可以將這些值組合成正確的定義,即:
foldMap f (Cons a ll) = mappend (f a) (foldMap f ll)
右側看不到Cons
(或Empty
) - 如果這讓您感到困惑,請考慮foldMap
將LinkedList
作為輸入(在這種特殊情況下是Cons a ll
參數),但它不是 output a LinkedList
,它改為在任意選擇的 Monoid m
中輸出一個值。
我希望這會有所幫助,但如果您以前沒有遇到過Monoid
可能會有點難以理解——尤其是如果您在不先了解 Monoid 的情況下嘗試理解Foldable
的話。 我強烈建議您進一步閱讀這些主題,包括以出色的Typeclassopedia作為起點(在這種情況下,主要是關於 Monoid 和 Foldable 的部分)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.