gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 07:22:39 25 4
gpt4 key购买 nike

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

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

最佳答案

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

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变成可以接受的东西 anyCreature .

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

但是,返回值是协变的,因为它们与值具有完全相同的语义:
val animal = getDuck()    // ok
val duck = getAnimal() // <-- ERROR

与之比较
val x = handleDuck(animal)  // <-- ERROR
val y = handleAnimal(duck) // ok

形象地说,您分配给函数参数,但来自它们的返回值。使用命名参数变得更加明显:
val x = handle(duck   = animal) // <-- ERROR
val y = handle(animal = duck ) // ok

关于scala - 为什么第一个类型参数在 Function1[-A, +B] 中被定义为逆变?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23308555/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com