【问题标题】:why the first type parameter is defined as contravariant in Function1[-A, +B]?为什么第一个类型参数在 Function1[-A, +B] 中定义为逆变?
【发布时间】:2023-03-04 19:47:01
【问题描述】:

全部 在阅读Scala covariance / contravariance question 及其由 Daniel Spiewak 提供的答案以及“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) Function1[A,B] <: function1>

【问题讨论】:

    标签: scala


    【解决方案1】:

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

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

    如果函数是协变的而不是逆变的,那么函数Duck =&gt; T 也将是Animal =&gt; T 的子类型,您可以使用doHigherOrder(handleAnyDuck)。这将是一个错误,因为在doHigherOrder 内部,handleAnyAnimal(anyAnimal) 表达式将(在运行时/评估时)归结为handleAnyDuck(anyAnimal),这显然是不正确的,因为可以处理任何Duck 的函数可能可以' t 处理任何Animal:

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

    此外,假设Creature &gt;: Animal,函数handleAnyCreature: Creature =&gt; T确实也是Animal =&gt; 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
    

    【讨论】:

      猜你喜欢
      • 2019-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-25
      相关资源
      最近更新 更多