为什么在Function1 [-A,+ B]中将第一个类型参数定义为逆变?

时间:2014-04-26 08:32:17

标签: scala

所有   在阅读了Daniel Spiewak提供的Scala covariance / contravariance question及其答案以及“Scala编程”一书的第19.3-19.7节后,我对Function1 [-A,+ B]的定义产生了另一种疑惑:为什么它的第一个类型参数逆变?我有一个原因,就是这个类型参数保证当有多种情况时,子类型总是出现在超类型之前,同时,子类型“是一个”超类型。例如:

 class A
 class B extends A
 class C extends B
 scala> val withDefault: A => B = {
 | case x:B => new B
 | case x:A => new B }
 withDefault: A => B = <function1>

这里,(1)case x:B早于case x:A,(2)功能1 [A,B]&lt ;: Function1 [B,B]   还有其他原因吗?任何信息都将不胜感激!

1 个答案:

答案 0 :(得分:9)

逆变法与模式匹配无关。这就是为什么函数类型在它们的参数类型上是逆变的。考虑这个功能:

def doHigherOrder(handleAnyAnimal: Animal => T,
                  anyAnimal:       Animal     ): T = {
  // ...foo...
  handleAnyAnimal(anyAnimal)
  // ...bar...
}

如果函数是协变而不是逆变,那么函数Duck => T也可以是Animal => T的子类型,你可以doHigherOrder(handleAnyDuck)。这将是一个错误,因为在doHigherOrder内,handleAnyAnimal(anyAnimal)表达式(在运行时/评估时)会归结为handleAnyDuck(anyAnimal),这显然是不对的,因为一个函数可以处理任何Duck可能无法处理任何Animal

def doHigherOrder(handleAnyDuck: Duck => T,
                  anyAnimal:     Animal   ): T = {
  // ...foo...
  handleAnyDuck(anyAnimal)  // <-- ERROR
  // ...bar...
}

此外,假设Creature >: Animal,函数handleAnyCreature: Creature => T确实也是Animal => T的子类型,因为将anyAnimal传递给可以接受{{1}的内容是可以的}}

这就是为什么逆变在参数类型中直观上是不可避免的。


但是,返回值是协变的,因为它们与语义具有完全相同的语义:

anyCreature

比较
val animal = getDuck()    // ok
val duck   = getAnimal()  // <-- ERROR

形象地说,您将分配给函数参数,但分配它们的返回值。使用命名参数变得更加明显:

val x = handleDuck(animal)  // <-- ERROR
val y = handleAnimal(duck)  // ok