任何与fmap具有相同多态类型的函数必须等于fmap?

问题描述 投票:4回答:3

我正在阅读Haskell编程的第二版,我发现了这句话:

...只有一种方法可以将任何给定的参数化类型转换为仿函数,因此任何与fmap具有相同多态类型的函数必须等于fmap

但这对我来说似乎并不合适。我可以看到每个fmap类型只有一个有效的Functor定义,但我肯定可以定义任何数量的(a -> b) -> f a -> f b类型的函数,它们彼此不相同?

为什么会这样?或者,这只是作者的错误吗?

haskell functor
3个回答
9
投票

你误读了作者所说的话。

...任何与fmap具有相同多态类型的函数...

这意味着,具有签名的任何功能

Functor f => (a -> b) -> f a -> f b

必须等同于qazxsw poi。 (当然,除非你允许底价值。)

这种说法是对的;如果你试图定义这样一个函数,很容易看出:因为你对fmap一无所知,只不过它是一个仿函数,获得非⊥f值的唯一方法是通过fazxswpoi来映射。

什么不那么清晰的是报价中的逻辑含义:

只有一种方法可以将任何给定的参数化类型转换为仿函数,因此任何与fmap具有相同多态类型的函数都必须等于fmap。

我认为作者的意思是,因为f b函数必须调用f a,并且因为Functor f => (a -> b) -> f a -> f b始终是参数化类型的唯一有效函子映射,所以任何fmap在实践中也确实遵守函子定律,即它将是fmap

我同意“因此”有点严厉,但原则上引用是正确的。


4
投票

我认为引用是指这种情况。假设我们定义一个参数化类型:

Functor f => (a -> b) -> f a -> f b

我们不仅可以编写一个,而且可以编写两个fmap实现

data F a = .... -- whatever

满足仿函数法则

fmap

在这些假设下,我们有fmap1 :: (a -> b) -> F a -> F b fmap2 :: (a -> b) -> F a -> F b

这是与fmap1 id = id fmap1 (f . g) = fmap1 f . fmap1 g fmap2 id = id fmap2 (f . g) = fmap2 f . fmap2 g 的多态类型相关的“自由定理”的理论结果(参见fmap1 = fmap2下的评论)。实际上,这确保了我们从fmap获得的实例是唯一可能的实例。


2
投票

这是一个错误。下面是一些与Lemma 1类型相同的函数示例,用于不是deriving Functor的列表:

fmap

还有更多。一般来说,给定一个函数fmap从列表长度到不大于该长度的数字列表(即列表中的有效索引),我们可以构建

\f -> const []
\f -> concatMap (replicate 2 . f)
\f -> map (f . head) . chunksOf 2
\f -> map f . reverse

使用适当的ixf懒惰变体来处理无限列表。可以为其他类似容器的仿函数制作类似的函数模式。

© www.soinside.com 2019 - 2024. All rights reserved.