问题描述
在学校里,我的任务是编写一个将数字附加到列表左侧(如果它们是偶数)的函数。类型签名为:
appendIfEven :: (Applicative f,Monoid (f a),Integral a) => a -> f a -> f a
我的回答是下面的代码
appendIfEven :: (Applicative f,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,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以及我错了....
解决方法
您无意中使用了一个意外的monoid,偶然使您的代码得以编译。
当您编写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解决了monoid约束,以便使用此实例。
这也意味着
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
,因此最终结果可能会非常不同。
考虑将其应用于列表。
mempty == []
pure 5 = [5]
那些看起来与我不太相似。特别是mempty x
的输入应错误。
基本上mempty
给您一个空的东西,而pure x
给您一个包含x
的东西。