我正在阅读《 Haskell编程》第二版,偶然发现了这句话:
...只有一种方法可以将任何给定的参数化类型转换为函子,因此具有与
fmap
相同的多态类型的任何函数都必须等于fmap
。
这对我来说似乎不对。我可以看到每种fmap
类型只有Functor
的 valid 定义,但是可以肯定的是,我可以用(a -> b) -> f a -> f b
类型定义任意数量的函数彼此不相等吗?
为什么会这样?或者,这仅仅是作者的一个错误?
答案 0 :(得分:9)
您误解了作者在说什么。
...具有与
fmap
相同的多态类型的任何函数 ...
这意味着具有签名的任何功能
Functor f => (a -> b) -> f a -> f b
必须等于fmap
。 (当然,除非您允许最低值。)
那句话是真的;如果您尝试定义这样的函数,则很容易看到:因为对f
一无所知,除了它是一个函子,获取非obtain f b
值的唯一方法是通过映射f a
。
引号中的逻辑含义不太清楚:
只有一种方法可以将任何给定的参数化类型转换为函子,并且因此与fmap具有相同多态类型的任何函数都必须等于fmap。
我认为作者的意思是,因为Functor f => (a -> b) -> f a -> f b
函数必须调用fmap
,并且因为fmap
始终是唯一的有效函子-在映射参数化类型时,实际上,任何Functor f => (a -> b) -> f a -> f b
也会遵守函子定律,即它将是 the fmap
。
我同意“因此”的措词有点不好,但原则上引用是正确的。
答案 1 :(得分:4)
我认为引言是指这种情况。假设我们定义了一个参数化类型:
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
获得的实例是唯一可能的实例。
答案 2 :(得分:2)
这是一个错误。对于不是fmap
的列表,下面是一些与fmap
类型相同的函数的示例:
\f -> const []
\f -> concatMap (replicate 2 . f)
\f -> map (f . head) . chunksOf 2
\f -> map f . reverse
还有更多。通常,给定功能ixf
,从列表长度到不大于该长度的数字列表(即列表中的有效索引),我们可以构建
maybeIt'sFmapLol :: (Int -> [Int]) -> (a -> b) -> [a] -> [b]
maybeIt'sFmapLol ixf elemf xs = [map elemf xs !! ix | ix <- ixf (length xs)]
使用Int
的适当惰性变体来处理无限列表。可以为其他类似容器的函子构造类似的功能模式。