函数的方差

Variance of function

sealed trait Sum[+A, +B] {
  def flatMap[A, C](f: B => Sum[A, C]): Sum[A, C] =
    this match {
      case Failure(v) => Failure(v)
      case Success(v) => f(v)
    }
} 

不是说函数参数是逆变的,结果是协变的吗?为什么编译器说 A 处于逆变位置?我希望编译器会抱怨 B 处于反变量位置。

有人可以向我解释为什么会这样吗?感觉很迷茫。

我假设你真的想写:

sealed trait Sum[+A, +B] {
  def flatMap[C](f: B => Sum[A, C]): Sum[A, C] = // No shadowing of A
    this match {
      case Failure(v) => Failure(v)
      case Success(v) => f(v)
    }
}

再看一下flatMap

def flatMap[C](f: B => Sum[A, C]): Sum[A, C]

让我们重写一下:

def flatMap[C]: (B => Sum[A, C]) => Sum[A, C]

让我们从内到外构建类型。

Sum[A, C]

ASum 的参数,通常是协变位置。

B => Sum[A, C]

Sum[A, C]是函数的结果,通常是协变位置。这两个结合起来,你仍然有 A 处于协变位置。

(B => Sum[A, C]) => Sum[A, C]

B => Sum[A, C]也是一个函数的参数,所以整个东西处于逆变位置。由于 A 之前处于协变位置,因此方差合并并且 A 现在处于逆变位置。

正在查看 B:

B => Sum[A, C]

函数的参数,通常是逆变位置。

(B => Sum[A, C]) => Sum[B, C]

整个函数也是另一个函数的参数,因此逆变抵消了,B 处于协变位置。

你也可以打个漂亮的比方。看一个协变和逆变类型参数的定义:

trait Co[+A]; trait Con[-A]

它们看起来像正数和负数,只是一点点。现在,请记住您在小学学习的乘法和符号规则:

  • (+) * (+) = (+)
  • (+) * (-) = (-)
  • (-) * (+) = (-)
  • (-) * (-) = (+)

这类似于(如果你眯着眼睛)

  • Co[Co[A]] => A 处于协变位置
  • Co[Con[A]] => A 处于逆变位置
  • Con[Co[A]] => A 处于逆变位置
  • Con[Con[A]] => A 处于协变位置。