函数在参数类型上是逆变的,在 return 类型上是协变的

Functions are contravariant in their argument types and co-variant in their return types

在发布这个问题之前我已经阅读了 this and this 个答案,但是我对这个主题的理解仍然有点不清楚,如下所述:

我理解协变和逆变独立的意思。

如果我有 类 如下:

class Car {}
class SportsCar extends Car {}
class Ferrari extends SportsCar {}

并且:

object covar extends App {

    // Test 1: Works as expected

    def test1( arg: SportsCar => SportsCar ) = {
        new SportsCar
    }

    def foo1(arg: Car): Ferrari = { new Ferrari }
    def foo2(arg: SportsCar): Car = { new Ferrari }
    def foo3(arg: Ferrari): Ferrari = { new Ferrari }

    test1(foo1) // compiles
    test1(foo2) // Fails due to wrong return type - violates return type is covariant
    test1(foo3) // Fails due to wrong parameter type - violates param type is contravariant

    // Test 2: Confused - why can I call test2 succesfully with ferrari 
    // as the parameter, and unsuccesfully with a car as the parameter?

    def test2(arg: SportsCar): SportsCar = {
        new Ferrari
    }

    val car = new Car()
    val sportsCar = new SportsCar()
    val ferrari = new Ferrari()

    val f1 = test2(ferrari)  // compiles - why?
    val f2 = test2(car)      // fails - why?

}

如前所述,在测试2中,为什么以ferrari为参数调用test2可以成功,以car为参数调用却不能成功?

语句函数在它们的参数类型上是逆变的并且在它们的return类型上是协变的是否仅适用于作为参数传递的函数?我想我没有适当区分语句和我编写的 2 个测试。

那是因为您将 子类型co/contravariance.

混用

在您的第一个示例中,您期望 Function1[-T, +R]。在这种情况下,co/contravariance 的规则适用于 函数类型 。在第二个示例中,您遵循简单的子类型化规则。

test1 中,你传入 foo1 的地方是 Car => Ferrari 一切正常,因为 foo1 期望 Car,但得到 [=16] =],这是一个Car。由于子类型的性质,任何需要 Car 的方法都可以处理子类型。但是当我们单独谈论子类型时,这些规则不起作用。

如果我们用 foo1 和实际类型展开 test1,也许会更清楚:

foo1:

  • 参数类型:
    • 预期:Car
    • 实际:SportsCar
  • Return 输入:
    • 预期:SportsCar
    • 实际:Ferrari

一切都很好。

test2 中,规则发生了变化。如果我期望 SportsCar 你传入 任何汽车 ,那么我不能再依赖于输入是跑车并且一切都坏了,但是如果你传入法拉利居然是跑车,什么都好

再次,让我们排列类型:

测试2:

  • 第一种情况:
    • 参数类型:
      • 预期:SportsCar
      • 实际:Ferrari(子类型)
    • 结果:大家都很开心
  • 第二种情况:
    • 参数类型:
      • 预期:SportsCar
      • 实际:Car
    • 结果:错误。我们不确定汽车是否是真正的跑车。