首页 > 解决方案 > L 型在 A 型中处于逆变位置 => Either[L, B]

问题描述

我尝试为 Either 编写简单的 flatMap 实现

sealed trait Either[+L, +R] {
  def flatMap[B](f: R => Either[L, B]): Either[L, B] = this match {
    case Left(e) => Left(e)
    case Right(e) => f(e)
  }
}

final case class Right[+A, +B](right: B) extends Either[A, B]
final case class Left[+A, +B](left: A) extends Either[A, B]

并面临以下问题:协变类型 L 在 f 类型中处于逆变位置:R => Either[L, B] of value f,但为什么会这样?当我们将变体类型作为函数的参数时,我认为我们的类型处于逆变位置,它与类型声明无关

标签: scalatypesvariancecontravariance

解决方案


您可以将其R => Either[L, B]视为“类型的广义值L” - 它与 an 不完全相同L,但给定 anR它可能会产生一个L. 因此,您的flatMap“使用类型的广义值L”。同时,您的方差声明声称在 中Either[+L, +R]是协变的L,因此, anEither[VerySpecial, R]必须是 的特例Either[RatherGeneral, R]。但这是不可能的,因为flatMap只能消耗VerySpecial值的 会阻塞RatherGeneral输入。

  • Either[+L, +R],L处于协变位置(它“产生” L s,至少有时)
  • R => Either[L, B],L仍处于协变位置(因为函数产生Either[L, B]Either[L, B]又产生Ls,所以整个事物产生Ls)
  • (R => Either[L, B]) => Either[L, B]中,第一个L出现在contra变体位置,因为参数部分被方法消耗flatMap

这很容易通过标准的下限类型技巧解决:

sealed trait Either[+L, +R] {
  def flatMap[B, M >: L](f: R => Either[M, B]): Either[M, B] = this match {
    case Left(e) => Left(e)
    case Right(e) => f(e)
  }
}

final case class Right[+A, +B](right: B) extends Either[A, B]
final case class Left[+A, +B](left: A) extends Either[A, B]

推荐阅读