我正在阅读Haskell编程的第二版,我发现了这句话:
...只有一种方法可以将任何给定的参数化类型转换为仿函数,因此任何与
fmap
具有相同多态类型的函数必须等于fmap
。
但这对我来说似乎并不合适。我可以看到每个fmap
类型只有一个有效的Functor
定义,但我肯定可以定义任何数量的(a -> b) -> f a -> f b
类型的函数,它们彼此不相同?
为什么会这样?或者,这只是作者的错误吗?
你误读了作者所说的话。
...任何与
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
。
我同意“因此”有点严厉,但原则上引用是正确的。
我认为引用是指这种情况。假设我们定义一个参数化类型:
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
获得的实例是唯一可能的实例。
这是一个错误。下面是一些与Lemma 1类型相同的函数示例,用于不是deriving Functor
的列表:
fmap
还有更多。一般来说,给定一个函数fmap
从列表长度到不大于该长度的数字列表(即列表中的有效索引),我们可以构建
\f -> const []
\f -> concatMap (replicate 2 . f)
\f -> map (f . head) . chunksOf 2
\f -> map f . reverse
使用适当的ixf
懒惰变体来处理无限列表。可以为其他类似容器的仿函数制作类似的函数模式。