为什么第一个类型参数在 Function1[-A, +B] 中被定义为逆变? [英] why the first type parameter is defined as contravariant in Function1[-A, +B]?

查看:37
本文介绍了为什么第一个类型参数在 Function1[-A, +B] 中被定义为逆变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

全部阅读Scala 协变/逆变问题及其由 Daniel Spiewak 提供的答案以及Scala 编程"一书的第 19.3-19.7 节后",我对 Function1[-A, +B] 的定义还有另一个困惑:为什么它的第一个类型参数是逆变的?我得到了一个原因,就是这个类型参数保证了当有多种情况时子类型总是比超类型出现得更早,同时,子类型是"超类型.例如:

all after reading Scala covariance / contravariance question and its answer provided by Daniel Spiewak,and Section 19.3-19.7 of book "Programming in Scala", I have another confusion about the definion of Function1[-A, +B]: why its first type parameter is contravariant? I got one reason, it is that this type parameter guarantees that the subtypes always appear earlier than supertypes when there are multiple cases, at the same time, subtype "is a" supertype. for example:

 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) Function1[A,B] <: Function1[B,乙]一定有其他原因吗?任何消息将不胜感激!

here, (1) case x:B is earlier than case x:A , (2) Function1[A,B] <: Function1[B,B] and there must be other reasons? any message will be appreciated!

推荐答案

逆变与模式匹配无关.这就是函数类型在其参数类型上逆变的原因.考虑这个函数:

Contravariance has nothing to do with pattern matching. Here's why function types are contravariant on their argument types. Consider this function:

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

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

If functions were covariant not contravariant, then a function Duck => T would also be a subtype of Animal => T and you could do doHigherOrder(handleAnyDuck). This would be an error because then, inside doHigherOrder, the handleAnyAnimal(anyAnimal) expression would (at runtime/evaluation) boil down to handleAnyDuck(anyAnimal), which is obviously not right because a function that can handle any Duck probably can't handle any Animal:

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

此外,假设Creature >:Animal,一个函数handleAnyCreature:Creature =>T 确实也是 Animal => 的一个子类型.T,因为可以将anyAnimal 传递给可以接受anyCreature 的东西.

Moreover, assuming Creature >: Animal, a function handleAnyCreature: Creature => T is indeed also a subtype of Animal => T, because it is OK to pass anyAnimal into something that can accept anyCreature.

这就是为什么对于参数类型来说逆变在直觉上是不可避免的.

That's why contravariance is intuitively inevitable with argument types.

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

However, return values are covariant because they have exactly the same semantics as just values:

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

比较

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

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

Figuratively, you assign to function arguments, but from their return values. It becomes more obvious with named arguments:

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

这篇关于为什么第一个类型参数在 Function1[-A, +B] 中被定义为逆变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆