为什么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
函数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
,这是一个 type 构造函数:它以一般 type a
和返回类型 LinkedList a
;
Cons
,它是一个 value 构造函数:它以一般类型 a
的 value 作为参数] 和类型 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 a
和 foldMap 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
) - 如果这让您感到困惑,请考虑 foldMap
将 LinkedList
作为 input(在这种特殊情况下是 Cons a ll
参数),但它不输出 LinkedList
,而是输出任意选择的 Monoid m
.
我希望这对您有所帮助,但如果您以前没有遇到过 Monoid
,那么理解起来可能会有点困难 - 特别是如果您试图在没有先了解的情况下理解 Foldable
理解幺半群。我强烈建议围绕这些主题进一步阅读,包括出色的 Typeclassopedia 作为起点(在这种情况下,主要是有关 Monoid 和 Foldable 的部分)。
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
函数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
,这是一个 type 构造函数:它以一般 typea
和返回类型LinkedList a
;Cons
,它是一个 value 构造函数:它以一般类型a
的 value 作为参数] 和类型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 a
和 foldMap 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
) - 如果这让您感到困惑,请考虑 foldMap
将 LinkedList
作为 input(在这种特殊情况下是 Cons a ll
参数),但它不输出 LinkedList
,而是输出任意选择的 Monoid m
.
我希望这对您有所帮助,但如果您以前没有遇到过 Monoid
,那么理解起来可能会有点困难 - 特别是如果您试图在没有先了解的情况下理解 Foldable
理解幺半群。我强烈建议围绕这些主题进一步阅读,包括出色的 Typeclassopedia 作为起点(在这种情况下,主要是有关 Monoid 和 Foldable 的部分)。