“类似的论点也表明,满足第一定律的任何Functor实例(fmap id = id)也会自动满足第二定律。实际上,这意味着只需要检查第一定律(通常是非常简单的归纳)以确保Functor实例有效。“
如果是这种情况,为什么我们甚至会提到第二个仿函法?
Law 1: fmap id = id
Law 2: fmap (g . h) = (fmap g) . (fmap h)
答案 0 :(得分:19)
虽然我不能提供证据,但我相信这说的是由于parametricity,只要第一部法律成立,类型系统就会执行第二定律。指定这两个规则的原因是,在更一般的数学设置中,您可能有一些类别 C ,其中完全可以定义从 C 到其自身的“映射” (即 Obj上的一对内部函数( C )和 Hom( C )分别遵守第一条规则但未遵守第二条规则,因此无法构成仿函数。
请记住,Haskell中的Functor
是 Hask 类别的endofunctors,甚至在数学上被认为是 Hask 上的endofunctor的所有内容都不能表达在Haskell中......参数多态的约束排除了能够指定一个对它映射的所有对象(类型)不一致的仿函数。
基于this thread,普遍的共识似乎是第二个定律来自Haskell Functor
实例的第一定律。 Edward Kmett says,
鉴于
fmap id = id
,fmap (f . g) = fmap f . fmap g
来自免费 fmap的定理。这篇文章在很长一段时间内作为一篇文章发表在一篇文章中,但我忘了在哪里。
答案 1 :(得分:12)
使用seq
,我们可以编写一个满足第一条规则但不满足第二条规则的实例。
data Foo a = Foo a
deriving Show
instance Functor Foo where
fmap f (Foo x) = f `seq` Foo (f x)
我们可以证明这符合第一定律:
fmap id (Foo x)
= id `seq` Foo (id x)
= Foo (id x)
= Foo x
然而,它打破了第二定律:
> fmap (const 42 . undefined) $ Foo 3
Foo 42
> fmap (const 42) . fmap undefined $ Foo 3
*** Exception: Prelude.undefined
尽管如此,我们通常会忽视这些病态病例。
答案 2 :(得分:3)
我会说出于有效性原因没有提到第二条法律,而是作为一项重要的财产:
第一条法律规定将身份功能映射到每个项目上 在容器中没有效果。第二个说映射a 容器中每个项目的两个函数的组合是 与首先映射一个函数,然后映射另一个函数相同。 --- Typeclassopedia
(我不明白为什么这第一定律意味着第二定律,但我不是熟练的Haskeller - 当你知道发生了什么时它可能是显而易见的)
答案 3 :(得分:2)
法律2似乎从法律1开始实现quite recently。因此,当文档最初编写时,它可能被认为是一个独立的要求。
(就个人而言,我不相信这个论点,但由于我没有时间自己弄清楚细节,我在这里给出了怀疑的好处。)