簡體   English   中英

為什么在 haskell 的類型類定義中不能使用類型構造函數?

[英]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函數mappenedmempty是在哪里定義的?

編輯 - - - - - - - - - - - - - - - - - - - - - - - - - --------------------

當我從 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)時,您嘗試結合fafoldMap 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 ) - 如果這讓您感到困惑,請考慮foldMapLinkedList作為輸入(在這種特殊情況下是Cons a ll參數),但它不是 output a LinkedList ,它改為在任意選擇的 Monoid m中輸出一個值。

我希望這會有所幫助,但如果您以前沒有遇到過Monoid可能會有點難以理解——尤其是如果您在不先了解 Monoid 的情況下嘗試理解Foldable的話。 我強烈建議您進一步閱讀這些主題,包括以出色的Typeclassopedia作為起點(在這種情況下,主要是關於 Monoid 和 Foldable 的部分)。

暫無
暫無

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

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