为何Bifunctor类型类的bimap无循环定义?如何理解其组合逻辑?
Haskell Bifunctor类型类关键疑问解答
先看定义代码:
class Bifunctor f where bimap :: (a -> c) -> (b -> d) -> f a b -> f c d bimap g h = first g . second h first :: (a -> c) -> f a b -> f c b first g = bimap g id second :: (b -> d) -> f a b -> f a d second = bimap id
1. first和second的组合逻辑:分工明确的类型参数映射
first只负责处理双函子的第一个类型参数:它接受a->c的函数,把f a b转成f c b——第二个类型参数b全程不变,所以返回值的第二个类型还是b。second只负责处理双函子的第二个类型参数:接受b->d的函数,把f a b转成f a d——第一个类型参数a全程不变,所以返回值的第一个类型还是a。
两者是“各司其职”的关系,一个改左类型,一个改右类型,返回值的类型差异就是因为各自只修改了对应的参数。
2. 看起来的循环是默认实现的依赖,不是真循环
Haskell类型类的方法允许提供默认实现,这里的三个方法是相互作为默认实现的基础,但这不是强制循环:
- 如果你为某个类型实现
Bifunctor时,自己写了bimap的具体逻辑,那么first和second会自动用bimap的默认实现(比如first g = bimap g id就是用id保留第二个参数,只映射第一个)。 - 如果你不想写
bimap,也可以自己实现first和second的具体逻辑,这时候bimap的默认实现会用first g . second h来组合——先处理第一个参数,再处理第二个,最终实现同时映射两个参数的效果。
你只需要实现其中至少一个(或一组)方法的具体逻辑,就能打破这个依赖链,不会出现循环。
3. 为什么bimap不会出现循环定义?
因为实例化Bifunctor时,必须提供非默认的具体实现来打破依赖。举个元组(,)的例子:
要么直接实现bimap:
instance Bifunctor (,) where bimap f g (x, y) = (f x, g y)
这时候first和second会基于这个bimap自动生成,完全没问题。
要么直接实现first和second:
instance Bifunctor (,) where first f (x, y) = (f x, y) second g (x, y) = (x, g y)
这时候bimap的默认实现会用first f . second g来组合,结果和直接写bimap一致,也没有循环。
只有当你完全依赖默认实现、不写任何具体逻辑时才会有循环,但Haskell不允许这种无效的实例定义。
内容的提问来源于stack exchange,提问作者qwertyman9938
相关产品推荐
相关产品推荐

