为什么 Scala 在包装 EitherT 时不能推断出正确的类型?
Why does scala not infer the right type when wrapping EitherT?
代码如下:
// eventually will be an implicit class with extension methods
class EitherTWrapper [L,R] (ei: EitherT[Future,L,R])
new EitherTWrapper(??? : EitherT[Future,Nothing,Boolean])
编译失败:
type mismatch;
found : cats.data.EitherT[scala.concurrent.Future,Nothing,Boolean]
required: cats.data.EitherT[scala.concurrent.Future,L,Boolean]
Note: Nothing <: L, but class EitherT is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
如果我明确提供类型,它会工作正常,如下所示:
new EitherTWrapper[Nothing,Boolean](??? : EitherT[Future,Nothing,Boolean])
这会起作用,但如果我试图使它成为一个隐含的 class。
我希望这能奏效。如何定义可以包装 EitherT 的 class?
如错误所示,将包装器更改为 class EitherTWrapper [+L,R] (ei: EitherT[Future,L,R])
将修复编译错误。
您的错误指出 Nothing <: L, but class EitherT is invariant in type A.
- 这意味着 Nothing
是 L
的子类型,因此声明 L
是非法的,因为它暗示您明确想要 L
不是它的子类型(即 L
是不变的)。
将某物声明为 +L
使其具有协变性,从而使您想要的成为可能。在 scala 文档中阅读更多关于方差的信息:https://docs.scala-lang.org/tour/variances.html
以下代码(来自文档)起作用的原因是因为 Scala 的 List
被定义为 List[+A]
,这意味着您还可以传入 List[Cat]
和 List[Dog]
到接受 List[Animal]
:
的函数
abstract class Animal {
def name: String
}
case class Cat(name: String) extends Animal
case class Dog(name: String) extends Animal
object CovarianceTest extends App {
def printAnimalNames(animals: List[Animal]): Unit = {
animals.foreach { animal =>
println(animal.name)
}
}
val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom"))
val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex"))
printAnimalNames(cats)
// Whiskers
// Tom
printAnimalNames(dogs)
// Fido
// Rex
}
显然,这是一个已知的 Scala 编译器错误(限制?):
https://github.com/scala/bug/issues/9453
似乎有 2 个解决方法:
- 使类型在包装器上协变(不太好,因为现在包装器与被包装的东西具有不同的变化行为,并且仅在类型参数未被逆变使用时才有效)。
- 通过为
EitherT
创建一个单独的包装器,特别处理 Nothing
的版本。这甚至在尝试将包装器用作隐式 类. 时也有效
代码如下:
// eventually will be an implicit class with extension methods
class EitherTWrapper [L,R] (ei: EitherT[Future,L,R])
new EitherTWrapper(??? : EitherT[Future,Nothing,Boolean])
编译失败:
type mismatch;
found : cats.data.EitherT[scala.concurrent.Future,Nothing,Boolean]
required: cats.data.EitherT[scala.concurrent.Future,L,Boolean]
Note: Nothing <: L, but class EitherT is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
如果我明确提供类型,它会工作正常,如下所示:
new EitherTWrapper[Nothing,Boolean](??? : EitherT[Future,Nothing,Boolean])
这会起作用,但如果我试图使它成为一个隐含的 class。
我希望这能奏效。如何定义可以包装 EitherT 的 class?
如错误所示,将包装器更改为 class EitherTWrapper [+L,R] (ei: EitherT[Future,L,R])
将修复编译错误。
您的错误指出 Nothing <: L, but class EitherT is invariant in type A.
- 这意味着 Nothing
是 L
的子类型,因此声明 L
是非法的,因为它暗示您明确想要 L
不是它的子类型(即 L
是不变的)。
将某物声明为 +L
使其具有协变性,从而使您想要的成为可能。在 scala 文档中阅读更多关于方差的信息:https://docs.scala-lang.org/tour/variances.html
以下代码(来自文档)起作用的原因是因为 Scala 的 List
被定义为 List[+A]
,这意味着您还可以传入 List[Cat]
和 List[Dog]
到接受 List[Animal]
:
abstract class Animal {
def name: String
}
case class Cat(name: String) extends Animal
case class Dog(name: String) extends Animal
object CovarianceTest extends App {
def printAnimalNames(animals: List[Animal]): Unit = {
animals.foreach { animal =>
println(animal.name)
}
}
val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom"))
val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex"))
printAnimalNames(cats)
// Whiskers
// Tom
printAnimalNames(dogs)
// Fido
// Rex
}
显然,这是一个已知的 Scala 编译器错误(限制?): https://github.com/scala/bug/issues/9453
似乎有 2 个解决方法:
- 使类型在包装器上协变(不太好,因为现在包装器与被包装的东西具有不同的变化行为,并且仅在类型参数未被逆变使用时才有效)。
- 通过为
EitherT
创建一个单独的包装器,特别处理Nothing
的版本。这甚至在尝试将包装器用作隐式 类. 时也有效