Haskell Functor implied law

你说的曾经没有我的故事 提交于 2019-12-03 04:45:13

While I can't give a proof, I believe what this is saying is that due to parametricity, the type system enforces the second law as long as the first law holds. The reason to specify both rules is that in the more general mathematical setting, you might have some category C where it is perfectly possible to define a "mapping" from C to itself (i.e. a pair of endofunctions on Obj(C) and Hom(C) respectively) which obeys the first rule but fails to obey the second rule, and therefore fails to constitute a functor.

Remember that Functors in Haskell are endofunctors on the category Hask, and not even everything that would mathematically be considered an endofunctor on Hask can be expressed in Haskell... the constraints of parametric polymorphism rule out being able to specify a functor which does not behave uniformly for all objects (types) which it maps.

Based on this thread, the general consensus seems to be that the second law follows from the first for Haskell Functor instances. Edward Kmett says,

Given fmap id = id, fmap (f . g) = fmap f . fmap g follows from the free theorem for fmap.

This was published as an aside in a paper a long time back, but I forget where.

Using seq, we can write an instance which satisfies the first rule but not the second one.

data Foo a = Foo a
    deriving Show

instance Functor Foo where
    fmap f (Foo x) = f `seq` Foo (f x)

We can verify that this satisfies the first law:

fmap id (Foo x)
= id `seq` Foo (id x)
= Foo (id x)
= Foo x

However, it breaks the second law:

> fmap (const 42 . undefined) $ Foo 3
Foo 42
> fmap (const 42) . fmap undefined $ Foo 3
*** Exception: Prelude.undefined

That said, we usually ignore such pathological cases.

I'd say the second law is not mentioned for validity reasons, but rather as an important property:

The first law says that mapping the identity function over every item in a container has no effect. The second says that mapping a composition of two functions over every item in a container is the same as first mapping one function, and then mapping the other. --- Typeclassopedia

(I can't see why this first law implies the second law, but I'm not a skilled Haskeller - its probably obvious when you know what's going on)

It seems that it was realized quite recently that law 2 follows from law 1. Thus, when the documentation was originally written, it was probably thought to be an independent requirement.

(Personally, I'm not convinced by the argument, but since I haven't had the time to work out the details myself, I'm giving it the benefit of doubt here.)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!