热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

在函数参数A=>B的逆变位置接受协变类型A

考虑协变类型参数 Acase class Foo[+A](a: A): def bar(a: A) = a // error: covariant type A occurs

考虑协变类型参数 A

case class Foo[+A](a: A):
def bar(a: A) = a // error: covariant type A occurs in contravariant position
def zar(f: A => Int) = f(a) // ok
|
This is contravariant position. Why is it ok?
Foo(41).zar(_ + 1) // : Int = 42

为什么zar当它出现在 中的逆变位置时它被接受为参数A => Int

回答

遵循@sarveshseri 的想法,我将提供一个直观的解释,而不是正式的解释。既是因为我不知道正式的细节,也是因为我希望这对读者更有帮助。

首先是一些免责声明:


  1. 我可能有错别字或一些错误,如果您注意到,请编辑答案。

  2. 如上所述,我要描述的心智模型是一个近似值,在编译时和运行时实际发生的情况会有所不同。

  3. 在这个答案中,我将提到可互换的类型和类。这是错误的,我自己已经在其他答案中指出了这一点。在这种情况下,是为了简化这种心理模型;但我建议在点击差异后,将类型和类的区别混合到该模型中:https : //typelevel.org/blog/2017/02/13/more-types-than-classes.html

  4. 连接到上一点,我将在我的示例中使用具体/简单类型。幸运的是,由于类型擦除和参数化,我将解释的内容适用于类型构造函数和其他复杂类型。

  5. 我还将交替使用方法和函数,这也是错误的:https : //docs.scala-lang.org/tutorials/FAQ/index.html#whats-the-difference-between-methods-and-functions

现在让我们开始吧。首先让我们假设,如果一个方法接受 aFoo那么它只能接受 type 的值,Foo而不能接受其他的值,期间。

然后让我们假设子类型实际上是“转换值的“隐式”函数。所以,B <: A只是B => A

并让我们想象一下,这样的“演员”是一个伪装,该值实际上是同一个,但看到不同的(这基本上是里氏原理)

因此,当您尝试将 a 传递B给需要 an 的方法时,A编译器将插入此隐式强制转换。这样在运行时,该方法接收的值看起来像是 type 之一A而不是 type 之一B;但值仍然是类型的B (实际上我们在这里讨论的是而不是类型,但我希望你能明白)

那么让我们看看你的协变类的方法barbaz方法会发生什么,Foo

因为Foo[Dog] <: Foo[Animal]我可以将前者转换为后者,那么鉴于Cat <: Animal我也可以将前者转换为后者。最后,我可以通过这个Cat伪装成Animalbar的方法Foo[Dog]伪装成Foo[Animal],但随后在运行时,我们会传递Cat给需要一个方法Dogkataplum!当然,除非这种方法总是为这种情况做好准备。

这就是为什么bar必须像[B >: A](b: B). 在这里,我们是说我们可以接受任何B编译器可以为其生成隐式强制转换函数A => B (与以前相反,由于Any此类类型和此类函数始终是可能的)。然后 的实现bar应该能够为该新类型工作B并在需要时使用 cast 函数。

这意味着前面的例子不会在运行时爆炸,因为我们可以Cat直接通过而不通过间接伪装;这工作,因为编译器总是能够推断B应该是Animal (的LUBCatDog),所以它会投的Cat作为Animal并将其传递给bar以及 cast 函数Dog => Animal

请注意,A => Bcast 函数的存在意味着编译器也可以创建该F[A] => F[B]函数,如果它F是协变的。

现在让我们看看会发生什么baz。同样,我们将 a 转换Foo[Dog]为 a Foo[Animal],然后我们将尝试baz使用一个Animal => Int应该在运行时工作的函数进行调用,因为我们甚至根本不需要伪装,我们可以直接将这样的函数传递给Foo[Dog]因为(Animal => Int) <: (Dog => Int)this 因为函数在他们的投入。

但这将如何运作?很简单,直觉告诉我,如果我能够处理/消费/接收/使用任何,Animal那么我应该能够处理任何,Dog因为那些是Animals,对吧?让我们看看它如何与我们的心智模型一起工作。

我有baz(Dog => Int)并且我有f(Animal => Int)编译器可以做的就是创建一个新函数g(Dog => Int) = cast(Dog => Animal) andThen f(Animal => Int)并使用它g

希望这会有所帮助,请随时留下任何问题。






推荐阅读
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 本文介绍了Java集合库的使用方法,包括如何方便地重复使用集合以及下溯造型的应用。通过使用集合库,可以方便地取用各种集合,并将其插入到自己的程序中。为了使集合能够重复使用,Java提供了一种通用类型,即Object类型。通过添加指向集合的对象句柄,可以实现对集合的重复使用。然而,由于集合只能容纳Object类型,当向集合中添加对象句柄时,会丢失其身份或标识信息。为了恢复其本来面貌,可以使用下溯造型。本文还介绍了Java 1.2集合库的特点和优势。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 本文介绍了一种求解最小权匹配问题的方法,使用了拆点和KM算法。通过将机器拆成多个点,表示加工的顺序,然后使用KM算法求解最小权匹配,得到最优解。文章给出了具体的代码实现,并提供了一篇题解作为参考。 ... [详细]
  • 本文介绍了利用ARMA模型对平稳非白噪声序列进行建模的步骤及代码实现。首先对观察值序列进行样本自相关系数和样本偏自相关系数的计算,然后根据这些系数的性质选择适当的ARMA模型进行拟合,并估计模型中的位置参数。接着进行模型的有效性检验,如果不通过则重新选择模型再拟合,如果通过则进行模型优化。最后利用拟合模型预测序列的未来走势。文章还介绍了绘制时序图、平稳性检验、白噪声检验、确定ARMA阶数和预测未来走势的代码实现。 ... [详细]
  • Introduction(简介)Forbeingapowerfulobject-orientedprogramminglanguage,Cisuseda ... [详细]
  • 本博文基于《Amalgamationofproteinsequence,structureandtextualinformationforimprovingprote ... [详细]
  • Thisworkcameoutofthediscussioninhttps://github.com/typesafehub/config/issues/272 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • ;(function(window){***[dateDiff算时间差]*@param{[typeNumber]}hisTime[历史时间戳, ... [详细]
author-avatar
sysv
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有