Haskell 中的 pure 和 mempty 有什么区别?

What is the difference between pure and mempty in Haskell?

在学校,我的任务是编写一个函数,将数字附加到列表的左侧,如果它们是偶数的话。类型签名为:

appendIfEven :: (Applicative f, Monoid (f a), Integral a) => a -> f a -> f a

我的回答是下面这段代码

appendIfEven :: (Applicative f, Monoid (f a), Integral a) => a -> f a -> f a
appendIfEven x ms = if x `mod` 2 == 0 then mempty x `mappend` ms else ms

Haskell可以编译我的代码,但是不能正常运行。经过一些试验,我将 mempty 切换为 pure :

appendIfEven :: (Applicative f, Monoid (f a), Integral a) => a -> f a -> f a
appendIfEven x ms = if x `mod` 2 == 0 then pure x `mappend` ms else ms

这很好用。但为什么 ?不应该 pure == mempty 吗? (在这种情况下) 这对我的导师来说似乎并不重要。但是,我真的很想了解更多关于 Haskell 以及我错在哪里....

考虑将其应用于列表。

mempty == []

pure 5 = [5]

那些看起来和我不太相似。特别是 mempty x 应该是 ill-typed.

基本上 mempty 给你一个 空的 东西,而 pure x 给你一个里面有 x 的东西。

您无意中使用了意想不到的幺半群,它偶然使您的代码编译通过。

当你写 mempty x `mappend` ms 时,ms 值的类型是 f a,它是一个幺半群。这会强制 mempty x 具有相同的类型,因为 mappend 需要两个相同类型的参数。因此我们必须

mempty x :: f a

这意味着,因为 x :: a

mempty :: a -> f a

很奇怪吧?

嗯,库中正好有一个实例

instance Monoid t => Monoid (u -> t) where
   mempty = \_ -> mempty
   ...

你无意中将 x 传递给了 mempty,因为 t 可以是 f a,而 u 可以是 a上面的例子。 Haskell 解决了幺半群约束,因此可以使用此实例。

这也意味着

mempty x `mappend` ms

相同
(\_ -> mempty) x `mappend` ms  -- this is the mempty for (f a), instead!

相同
mempty `mappend` ms   -- this is the mempty for (f a), instead!

相同
ms

因此,您的代码完全忽略了 x 参数。

相比之下,pure x 在一般情况下确实依赖于 x,因此最终结果可能会大不相同。