scala中的协方差和方差翻转

covariance and variance flip in scala

Scala for the Impatient据说

函数的参数是逆变的,结果类型是协变的

这很简单易懂,但是在同一个主题中它说

但是在函数参数内部,方差翻转-它的参数是协变的

foldLeft method of Iterator为例 如:

 def foldLeft[B](z : B)(op : (B, A) => B) : B 

我不是很清楚它说的是什么。

我尝试了一些博客作为

  1. http://www.artima.com/pins1ed/type-parameterization.html

  2. http://blog.kamkor.me/Covariance-And-Contravariance-In-Scala/

  3. http://blogs.atlassian.com/2013/01/covariance-and-contravariance-in-scala/

但是没有搞清楚。

归结为一个函数是另一个函数的子类型意味着什么。如果 C 是 A 的子类型(输入类型中的逆变)并且 B 是 D 的子类型(return 类型中的协变),那么听起来您对 A->B 是 C->D 的子类型感到满意.

现在考虑将其他函数作为参数的函数。例如,考虑 (A->B)->B。我们只是两次应用相同的推理。参数是 A->B 类型的函数,return 类型是 B。提供 C->B 类型的函数作为输入类型需要什么?由于函数在输入类型中是逆变的,因此 C->B 必须是 A->B 的子类型。但正如我们在第一段中讨论的那样,这意味着 A 必须是 C 的子类型。因此在第一段中的推理两次应用之后,我们发现 (A->B)->B 在 A 位置上是协变的。

你可以用更复杂的函数进行类似的推理。事实上,您应该说服自己,如果某个位置位于偶数个箭头的左侧,则该位置是协变的。

首先将函数视为 class 或 typeclass 。想想它的类型Function1[-A,+B] 假设我们有以下内容,

class x<br> class y 扩展 b

现在我有两个函数,如下所示,

val test1:x=>Int = //do something
val test2:y=>int = //do something

现在如果我有像下面这样的另一种方法,

def acceptFunction(f: y => Unit, b: B) = //do something

根据类型签名 Function1[-A,+B] 我可以将 test2 传递给 acceptFunction 以及 test1 因为 contravariance。 有点像 test1<:test2 .

这与说函数的参数是协变完全不同。这意味着如果您有以下内容,

class  Fruit { def name: String="abstract" }
class Orange extends Fruit { override def name = "Orange" }
class Apple extends Fruit { override def name = "Apple" }

你可以这样写,

 testM(new Apple())
 def testM(fruit:Fruit)={}

但是你不能这样写,

     testM(new Fruit())
     def testM(fruit:Apple)={}

一个函数在它的参数类型上总是逆变的,在它的return类型上总是协变的 例如

trait Function1[-T1, +R] extends AnyRef 

trait Function2[-T1, -T2, +R] extends AnyRef 

这里,T1,T2, ..., Tn(其中 n <= 22)是参数,R 是 return 类型 .

在高阶函数(以函数为参数的函数)中,参数可以具有传递给函数的参数类型 例如 foldLeft 特征 Iterable

Iterable 声明为

trait Iterable[+A] extends AnyRef

foldLeft 贴标为

def foldLeft[B](z : B)(op : (B, A) => B) : B 

由于 A 被声明为协变的,它可以用作 return 类型。但这里它是一个参数类型,因为

trait Function2[-T1, -T2, +R] extends AnyRef 

因为op : (B, A) => BFunction2的文字类型。

关键是特征Function2在其参数类型中是逆变的.

因此,由于

,协方差类型出现在方法参数中

trait Function2 is contravariant in its argument type

这叫做方差翻转:

  1. 协方差的翻转是逆变。
  2. 逆变的翻转是协方差。
  3. 翻转是不变的是不变的。

这就是为什么不变量可能出现在任何位置(covariance/contravariance)