Scala 不应该’;难道不是协变的吗?尤其是在左边?
我有以下代码Scala 不应该’;难道不是协变的吗?尤其是在左边?,scala,functional-programming,scala-cats,Scala,Functional Programming,Scala Cats,我有以下代码 def addKitten(kitten: Kitten): EitherT[Future, GenericError, UUID] = { val futureOfEither = db.run { // returns a Future[Int] with the number of rows written kittens += kitten }.map { case 1 => kitten.uuid.asRight case _ =&g
def addKitten(kitten: Kitten): EitherT[Future, GenericError, UUID] = {
val futureOfEither = db.run { // returns a Future[Int] with the number of rows written
kittens += kitten
}.map {
case 1 => kitten.uuid.asRight
case _ => GenericError.SpecificError.asLeft
}
EitherT(futureOfEither)
}
其中SpecificError
是GenericError
的子类。出于某种原因,它不会编译,抱怨特定错误
不是一般错误
。是这样吗
我的意思是,
或者[A,B]
应该是不可变的,那么为什么不让它协变呢?我遗漏了什么吗?好的,我找到了解决办法
我仍然不知道为什么EitherT
不是协变的,但是你必须记住或者本身是协变的。
因此,诀窍是告诉编译器为EitherT
创建使用上限:
EitherT[Future, GenericError, UUID](futureOfEither)
保留多个错误也可以(因为编译器被迫找到LUB),但是GenericError
必须扩展Product
和Serializable
,如果这是一个特征,并且SpecificError
是一个case类(请参阅)
对XorT
和option
提出了相同的问题。答复是:
在Scala中,方差既有正数也有负数(也许这就是他们决定使用方差表示法的原因!:p)。这些优点/缺点在不同的场合已经讨论过无数次,所以我现在就不讨论了,但在我看来,在一天结束时,你必须“各自为政”
我认为这种“各自为政”的观点意味着不能强制类型构造函数发生变化。一个具体的例子是7.0系列中的scalaz.Free
。它强制S
类型构造函数是协变的。当时我经常想把一个Coyoneda
包装成Free
。最新版本的Cats和Scalaz将Coyoneda
基本内置于Free
中,因此这种特殊用途现在可能并不理想,但一般原则适用。问题是,Coyoneda
是不变的,所以你根本无法做到这一点(没有一堆@uncheckedVariance
)!通过使类型构造函数参数不变,最终可能会迫使人们对类型更加明确,但我认为这比另一种方法要好,在这种方法中,您可以阻止他们使用您的类型
作为另一个非常简洁的解决方法,您可以在flatMap[E,T]
type参数中指定抽象类型,例如:
// Given ADT
trait Err
case class E1() extends Err
case class E2() extends Err
// We could do (pseudo-code)
EitherT(E1()).flatMap[Err, Int] { x => 100 }
这是在FlatMap
的情况下。对于Map
,您只能在右侧转换值和输入 在Scala Future中,已经包装了一个Scala.util.Try
,它可能是Error
或其他什么,我不确定为什么有时候会选择Future[nother]
,但除了更多的CPU周期和运行时复杂性之外,它没有带来任何好处。@flavian Try的“失败”总是一个不明确的“一次性”而“未来”则是“未来”[Orther]“允许您为错误添加类型约束。您可以将特定于应用程序的错误移到“Orther”并保留“Future.failed”用于致命的基础结构错误,从而在致命错误和预期错误之间实现清晰的分离。您已经可以使用异常
和可丢弃
,因此它仍然只是样板文件。Scala还为您提供Scala.util.control.NonFatal
。
// Given ADT
trait Err
case class E1() extends Err
case class E2() extends Err
// We could do (pseudo-code)
EitherT(E1()).flatMap[Err, Int] { x => 100 }