"类似的论证也表明,任何满足第一个定律(fmap id = id)的Functor实例都将自动满足第二个定律。从实际上讲,这意味着只需要检查第一个定律(通常通过非常简单的归纳法完成)即可确保Functor实例是有效的。"
如果是这样的话,为什么我们还要提到第二个Functor定律呢?
Law 1: fmap id = id
Law 2: fmap (g . h) = (fmap g) . (fmap h)
"类似的论证也表明,任何满足第一个定律(fmap id = id)的Functor实例都将自动满足第二个定律。从实际上讲,这意味着只需要检查第一个定律(通常通过非常简单的归纳法完成)即可确保Functor实例是有效的。"
如果是这样的话,为什么我们还要提到第二个Functor定律呢?
Law 1: fmap id = id
Law 2: fmap (g . h) = (fmap g) . (fmap h)
虽然我无法提供证明,但我认为这句话的意思是,由于 参数多态性,只要第一条规则成立,类型系统就强制执行第二条规则。之所以指定两个规则,是因为在更一般的数学设置中,你可能有一些类别C,可以定义从C到其自身的“映射”(即Obj(C) 和 Hom(C) 上的一对自函数),该映射遵循第一个规则但不遵循第二个规则,因此不能构成一个函子。
请记住,Haskell 中的Functor
是Hask类别上的自函子,并且甚至不能用Haskell来表达在数学上被认为是Hask上的自函子的所有东西......参数多态性的约束排除了能够为所有对象(类型)均按一致方式进行映射的函子的指定。
根据此线程,普遍共识是第二条规则对于HaskellFunctor
实例来说是由第一条规则推导而来的。Edward Kmett 表示,
鉴于
fmap id = id
,fmap (f . g) = fmap f . fmap g
遵循 fmap 的自由定理。这曾经在一篇论文中作为旁注发表过,但我忘记了具体位置。
使用 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
话虽如此,我们通常会忽略这样的病态情况。