微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么类型构造函数不能在 haskell 的类型类定义中使用?

如何解决为什么类型构造函数不能在 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)
   |                              ^^^^^^^^^^
            
            

搜索一个解决方案,似乎我应该使用 'mapped' 而不是构造函数。这是非常令人困惑的。我没有在任何地方定义任何 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 作为参数并返回类型 {{1 }};
  • LinkedList a,它是一个 value 构造函数:它接受一个普通类型 Consvalue 和一个 value 类型为 a

另一方面,这里

LinkedList a

您将 foldMap f (a `Cons` ll) = LinkedList (f a) (foldMap f ll) 用作 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 类型,但它具有实际类型 { {1}},或者可能(事实证明)反过来。那么让我们自己看看类型。

它以 LinkedList m 的类型开头,我们可以在 documentation 中轻松找到:

foldMap

这意味着对于任何 Monoid foldMap :: Monoid m => (a -> m) -> t a -> m (无论调用者选择哪个特定 Monoid 作为函数 foldMap f ll 的返回类型),m 都将具有类型 m

同时,f 的类型为 f,因此 a -> m 的类型必须为 f a - 同样对于 m 选择的任意 Monoid 实例.因此,当 f 用作 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 类型。

(旁白:GHC 在这一点上实际上并没有放弃,它假设类型实际上彼此相等并查看导致的结果。但在这里它只会导致无限回归:相关的值必须具有类型LinkedList m 等于 m 等于 LinkedList m 等等。Haskell 在值定义中可以像这样无限回归,但不允许它用于类型 - 因此“不能构造无限类型".)

我们如何修复此错误并获得正确的 LinkedList (LinkedList m) 定义?您尝试将 foldMapf a 结合起来计算 foldMap f ll 的想法绝对正确。对于 foldMap f (Cons a ll) 所针对的特定(任意)Monoid,这两个都是 m 类型。我们不一定将它们与“普通函数”结合起来,比如 f,因为我们不知道 (+) 是什么类型 - 它不一定是数字类型,或者其他什么.但是我们知道的一件事是它是 Monoid 的一个实例 - 因此我们确实可以使用 m 方法 Monoid 组合任意两个这样的值。事实上,我们真的无能为力,因为我们一无所知关于这里的 mappend 类型除了它是 {{1} 的一个实例} - 因此有 m(和 Monoid)可用。

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

mappend :: m -> m -> m

右侧没有 mempty :: m(或 foldMap f (Cons a ll) = mappend (f a) (foldMap f ll) ) - 如果这让您感到困惑,请考虑 ConsEmpty 作为 输入(在这种特殊情况下是 foldMap 参数),但它不输出 LinkedList,而是在任意选择的 Monoid Cons a ll 中输出一个值。

我希望这会有所帮助,但如果您之前没有接触过 LinkedList,可能会有点难以理解 - 特别是如果您在没有先了解 Monoids 的情况下就试图了解 m .我强烈建议围绕这些主题进一步阅读,包括以出色的 Typeclassopedia 作为起点(在这种情况下,主要是关于 Monoid 和 Foldable 的部分)。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。