什么是反变函子:Contravariant Functor 的直觉解释
时间:2026-08-21 | 作者:宇宙开黑者 | 阅读:0contra variant
contra variant是一个定义了contramap函数的functor
def contramap[A, B](fa: F[A])(f: B => A): F[B]
可以发现,这跟普通的map很像。
区别在于,f函数的方向相反。
def map[A, B](fa: F[A])(f: A => B): F[B]
因此叫contramap。
平时所说的定义了map的functor,可以叫covariant functor。定义了contramap的是contra variant functor。
contra variant的例子有Show和scala.math.Ordering。
contramap的intuition
我们都知道,map可以把函数f应用到一个容器里面。这是map的intuition。
那么,contramap的intuition是什么?
先看contramap的参数。
F[A]:这是一个容器里面装着AB => A:这是一个把B转换成A的函数
很明显,不能把这个函数直接应用进去,因为容器装的不是B。
所以,这里先忘掉map intuition中的容器。
换一个角度看,contramap会非常直观。
有一天,一位数学家感觉自己对数学已经厌倦到了极点,于是他跑去消防队,宣称自己想要成为一名消防员。消防队长说:“您看起来很不错,不过我得先给您做个测试。”消防队长带着数学家来到消防队后院的小巷,巷子里有一个货栈、一只消防栓和一卷软管。消防队长问道:“假如货栈着火了,您会怎么做?”数学家回答:“我会把消防栓接到软管上,打开水龙头,把火扑灭。”消防队长说:“完全正确!最后一个问题:假如您走进小巷,而货栈没有着火,您会怎么做?”数学家疑惑地思考了很久,最后回答道:“那我就把货栈点燃。”消防队长大惊失色:“什么?太可怕了!您为什么要把货栈点燃?”数学家回答:“这样我就把问题简化成了一个我已经解决过的问题。”
这就是contramap的核心直觉:把一个新问题,化简成一个已经解决过的问题。
Show例子
再看一个Show的例子。
case class Money(amount: Int)
case class Salary(size: Money)
implicit val showMoney: Show[Money] = Show.show(m => s"$$${m.amount}")
implicit val showSalary: Show[Salary] = showMoney.contramap(_.size)
上面两个例子中的共同点,都是把一个问题化简为一个已经解决过的问题。
当已经定义了Show[Money]之后,再定义Show[Salary]时,就可以通过Salary.size方法把Salary转换成Money。
这样,问题就自动解决了。
contramap的应用
scala.math.Ordering中已经有contramap了。
def by[T, S](f: T => S)(implicit ord: Ordering[S]): Ordering[T]
这里的by,其实就是contramap。
- 要求解一个未知的问题:
Ordering[T] - 需要一个已解决的问题:
Ordering[S] - 还需要一个转换的方法:
T => S
Subtyping和contra variant的关系
相信大家都了解里氏替换原则吧。
简单来说,如果一个函数所需要的参数类型是A,那么A的所有子类型B都能够作为参数传入该函数。
比如说,有一个函数是getAge(human: Human),那么我们既可以传入getAge(man),也可以传入getAge(woman)。
Contra variant有这样的一个属性:
如果有一个contra variant Functor F,并且B <: A,那么F[A] <: F[B]。
背后的intuition
A可以看作是全人类,B可以看作是某一类人。
如果有一个问题,需要关于B这一类人的解决方案。那么如果我有关于A的解决方案,我一样可以套用进去。
again,F[B]可以看成是关于类型B的解决方案。
- Show[B]是解决如何print B
- Ordering[B]是如何对B排序
实现方法
实现方法非常简单。
class A
class B extends A
val showA: Show[A] = Show.show(a => "a!")
val showB: Show[B] = showA.contramap(identity[A])
一般情况下,在contramap需要提供如何从B转换成A的方法。
但是在这里,B是A的subtype,所以根本就不需要转化。
只需要告诉编译器,我就是一个A。
看回人类的那个例子。
例如我需要“测量男人年龄”的方案,然后我已经有一个“测量人类年龄”的方案,我只需要说“男人也是人类”,这个问题就自动解决了。
因此F[A] <: F[B],所有的F[B]可以用F[A]代替。
免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。
相关文章
更多-
- 迅捷路由器怎么调信号最强,设置时要注意什么?
- 时间:2026-08-27
-
- vivo浏览器怎么卸不掉?原因和解决方法在这里
- 时间:2026-08-27
-
- OPPO R11s黑屏了,怎么强制恢复出厂设置?
- 时间:2026-08-27
-
- 飞利浦显示器包装盒有生产日期和保修期吗?怎么看?
- 时间:2026-08-27
-
- 联想新平板开机必须联网吗?怎么做?
- 时间:2026-08-27
-
- 平板横竖屏切换设置与问题解决
- 时间:2026-08-27
-
- 移动电源容量怎么测?要准备哪些工具?
- 时间:2026-08-27
-
- 荣耀90 Pro防水吗?防水级别多少?怎么用才安全
- 时间:2026-08-27
精选合集
更多大家都在玩
大家都在看
更多-
- 糖尿病完全不能吃糖吗
- 时间:2026-09-15
-
- 蚂蚁庄园小课堂2026年9月16日最新题目答案
- 时间:2026-09-15
-
- 小鸡答题今天的答案是什么2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园每日答题答案2026年9月16日
- 时间:2026-09-15
-
- 以下哪种粮食是酿造绍兴黄酒的主要原料 蚂蚁庄园今日答案9月16日
- 时间:2026-09-15
-
- 劝学名句“及时当勉励,岁月不待人”出自哪位诗人 蚂蚁庄园今日答案9.16
- 时间:2026-09-15
-
- 蚂蚁庄园今天答题答案2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园答题今日答案2026年9月16日
- 时间:2026-09-15
