首页 > 解决方案 > Scala:every.joinRight 的类型推断问题

问题描述

我有以下类型:

trait A
trait A1 extends A
trait A2 extends A

标准 Scala 的 Either 具有以下方法:

def joinRight[A1 >: A, B1 >: B, C](implicit ev: B1 <:< Either[A1, C]): Either[A1, C]

这使我可以简化嵌套的 Either 类型。

如果我有以下两个值:

val x: Either[A, Either[A, Int]] = Right(Right(1))
val y: Either[A1, Either[A2, Int]] = Right(Right(1))

我可以joinRight很好地调用 x,它给了我一个实例Either[A, Int],但是如果我在 y 上调用 joinRight,我会得到:

错误:无法证明 Either[A1,Int] <:< scala.util.Either[A2,C]。x.joinRight

这是有道理的,因为要么是:Either[+A, +B]

现在,如果我要手动传递类型参数,即:

x.joinRight[A, Either[A, Int], Int]

它会工作并给我一个Either[A, Int]. 它无法推断它是非常令人失望的。

现在我有以下替代实现,如下所示:

def joinRightAlt[AA, BB](implicit ev1: B <:< Either[AA, BB], ev2: A <:< AA): Either[AA, BB] 

它也遇到同样的问题。即它可以处理x,但不能处理y。

三个问题:

标签: scalatype-inferencetypecheckingtype-constraints

解决方案


提供所有参数的一种替代方法是类型归属:

y.joinRight : Either[A, Int]
// res: Either[A, Int] = Right(1)

这相当于将其分配给具有显式类型的变量,例如:

val yy: Either[A, Int] = y.joinRight
// yy: Either[A, Int] = Right(1)

但是,如果你真的希望它被自动推断,你可以使用这种扩展方法。
诀窍是,在范围内同时拥有两种类型A1& A2。并且,强制编译器同时查找这两种类型的LUB

implicit class EitherOps[A1, A2, B](private val e: Either[A1, Either[A2, B]]) extends AnyVal {
  def joinRightAlt[AA](implicit ev: (A1, A2) <:< (AA, AA), f1: A1 => AA, f2: A2 => AA): Either[AA, B] =
    e match {
      case Left(a1)        => Left(f1(a1))
      case Right(Left(a2)) => Left(f2(a2))
      case Right(Right(b)) => Right(b)
    }
}

val x: Either[A, Either[A, Int]] = Right(Right(1))
val y: Either[A1, Either[A2, Int]] = Right(Right(1))
val y1: Either[A1, Either[A2, Int]] = Left(new A1 {})
val y2: Either[A1, Either[A2, Int]] = Right(Left(new A2 {}))

x.joinRightAlt
// res: Either[A, Int] = Right(1)

y.joinRightAlt
// res: Either[A, Int] = Right(1)

y1.joinRightAlt
// res: Either[A, Int] = Left(...)

y2.joinRightAlt
// res: Either[A, Int] = Left(...)

(请记住,在最坏的情况下,此方法将推断Any for AA


推荐阅读