为什么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 构造函数似乎不在范围内?为什么?

编译器错误:

        Data constructor not in scope: LinkedList :: m -> m -> m
   |        
17 |   foldMap f (a `Cons` ll) =  LinkedList (f a)  (foldMap f ll)
   |                              ^^^^^^^^^^
            
            

我搜索了一个解决方案,似乎我应该使用 'mappened' 而不是构造函数。这非常令人困惑。我没有在任何地方定义任何 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,这是一个 type 构造函数:它以一般 type a 和返回类型 LinkedList a;
  • Cons,它是一个 value 构造函数:它以一般类型 avalue 作为参数] 和类型 LinkedList a.
  • value

另一方面,这里

  foldMap f (a `Cons` ll) =  LinkedList (f a)  (foldMap f ll)

你正在使用 LinkedList 就好像是一个 value 构造函数,这是错误的。

编译器错误消息应包含足够的详细信息,以便您找出代码中的问题:

     • 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 应该有类型 m 当它有实际输入 LinkedList m,或者可能(事实证明)相反。那么我们自己看看类型吧。

foldMap的类型开头,我们很容易在documentation中找到:

foldMap :: Monoid m => (a -> m) -> t a -> m

这意味着 foldMap f ll 将具有类型 m,对于任何 Monoid m(无论调用者选择哪个特定的 Monoid 作为函数的 return 类型 f).

与此同时,f 的类型为 a -> m,因此 f a 的类型必须为 m - 对于 [=21 选择的任意 Monoid 实例同样如此=].因此,当 f a 用作 Cons 的第一个参数时 - Cons (f a) (foldMap f ll) - 我们可以比较来自类型定义的 Cons 的定义:

data LinkedList a = Empty | Cons a (LinkedList a)

告诉我们,由于 f a 的类型为 m,因此 Cons (f a) (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) 时尝试结合 f afoldMap f ll 的想法绝对正确。对于 f 所针对的特定(任意)幺半群,它们都是 m 类型。我们不一定将它们与 (+) 之类的“普通函数”结合起来,因为我们不知道 m 是什么类型——它不一定是数字类型或其他类型.但我们确实知道的一件事是它是 Monoid 的一个实例 - 因此我们确实可以将任何两个这样的值与 Monoid 方法 mappend 结合起来。事实上,我们真的不能做其他事情,因为我们 nothing 关于类型 m 这里 except 它是一个Monoid 的实例 - 因此有 mappend :: m -> m -> m(和 mempty :: m)可用。

所以只有一种方法可以将这些值组合成正确的定义,即:

foldMap f (Cons a ll) = mappend (f a) (foldMap f ll)

右侧没有 Cons(或 Empty) - 如果这让您感到困惑,请考虑 foldMapLinkedList 作为 input(在这种特殊情况下是 Cons a ll 参数),但它不输出 LinkedList,而是输出任意选择的 Monoid m.

我希望这对您有所帮助,但如果您以前没有遇到过 Monoid,那么理解起来可能会有点困难 - 特别是如果您试图在没有先了解的情况下理解 Foldable理解幺半群。我强烈建议围绕这些主题进一步阅读,包括出色的 Typeclassopedia 作为起点(在这种情况下,主要是有关 Monoid 和 Foldable 的部分)。