scala - 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。
三个问题:
编译器的两种实现有何不同?编译器检查类型约束所要做的工作有很大不同吗?
为什么替代实现找不到正确的
EE
和AA
实例?我们真的没有另一个可以处理的实现
y
吗?这在 Dotty 中是否可能/更容易?
解决方案
提供所有参数的一种替代方法是类型归属:
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
)。
推荐阅读
- python - 解析 JSON 以创建函数
- powershell - 使用变量名 powershell 访问对象
- c# - 为什么 EF Core 最后要添加一个额外的 ORDER BY
- flutter - 使用 TextFormField -> onSaved 时将字符串值设置为类
- android - 无法在 Android 中解析符号 repeatOnLifecycle
- angular - 如何编写角度测试用例来覆盖 array.find()
- python - 通过 Python Socket 库向研华 ADAM-6024 发送命令和接收数据
- r - Facet_grid:将条形文本的位置更改为顶部并更改颜色
- c# - 向 API 调用添加和填充 IEnumerable 属性
- firebase - 如何从firebase颤振中的文档中检索列表?