Haskell Functor暗示法律

时间:2011-11-29 04:43:25

标签: math haskell functor

Typeclassopedia说:

“类似的论点也表明,满足第一定律的任何Functor实例(fmap id = id)也会自动满足第二定律。实际上,这意味着只需要检查第一定律(通常是非常简单的归纳)以确保Functor实例有效。“

如果是这种情况,为什么我们甚至会提到第二个仿函法?

Law 1: fmap id = id
Law 2: fmap (g . h) = (fmap g) . (fmap h)

4 个答案:

答案 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 = idfmap (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。因此,当文档最初编写时,它可能被认为是一个独立的要求。

(就个人而言,我不相信这个论点,但由于我没有时间自己弄清楚细节,我在这里给出了怀疑的好处。)