不确定这是一个错误,但以下演示在最终案例中失败:
import spray.json._
import DefaultJsonProtocol._
object SprayTest {
1.toJson
"".toJson
(Left(1): Either[Int, String]).toJson
(Right(""): Either[Int, String]).toJson
Seq(1).toJson
Seq("").toJson
Seq(Left(1), Right("")).toJson
Seq(Left(1), Right("")).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat)))
}
因此所有构建基块似乎都有效,但Seq
和Either
格式的构成失败了,即使我尝试用勺子喂它。
我看到以下错误:
[error] SprayTest.scala:11: Cannot find JsonWriter or JsonFormat type class for Seq[Product with Serializable with scala.util.Either[Int,String]]
[error] Seq(Left(1), Right("")).toJson
[error] ^
[error] SprayTest.scala:12: type mismatch;
[error] found : spray.json.DefaultJsonProtocol.JF[Either[Int,String]]
[error] (which expands to) spray.json.JsonFormat[Either[Int,String]]
[error] required: spray.json.JsonFormat[Product with Serializable with scala.util.Either[Int,String]]
[error] Note: Either[Int,String] >: Product with Serializable with scala.util.Either[Int,String] (and spray.json.DefaultJsonProtocol.JF[Either[Int,String]] <: spray.json.JsonFormat[Either[Int,String]]), but trait JsonFormat is invariant in type T.
[error] You may wish to define T as -T instead. (SLS 4.5)
[error] Seq(Left(1), Right("")).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat)))
知道是什么给出了什么?
答案 0 :(得分:14)
这是关于Either
最烦人的事情之一 - Left
和Right
构造函数都扩展了Product
和Serializable
,但Either
}本身并不会导致糟糕的推断类型:
scala> Seq(Left(1), Right(""))
res0: Seq[Product with Serializable with scala.util.Either[Int,String]] = List(Left(1), Right())
由于JsonFormat
在其类型参数中不变,因此您拥有A
实例的事实并不意味着您拥有Product with Serializable with A
的实例。在具体情况下,实际上有Either[Int, String]
的实例,但推断类型中的额外垃圾意味着编译器无法找到它。
如果你的序列中没有Right
,会发生类似的事情:
scala> Seq(Left(1), Left(2)).toJson
<console>:18: error: Cannot find JsonWriter or JsonFormat type class for Seq[scala.util.Left[Int,Nothing]]
Seq(Left(1), Left(2)).toJson
^
您可以通过提供类型而不是使用推断的类型来解决这两个问题:
scala> val xs: Seq[Either[Int, String]] = Seq(Left(1), Right(""))
xs: Seq[Either[Int,String]] = List(Left(1), Right())
scala> xs.toJson
res1: spray.json.JsValue = [1,""]
在许多情况下,这不是一个问题,因为您经常会从明确返回Either
而非使用Either
的方法中获取Left
值, Right
直接导致此问题。
作为脚注:这就是为什么当您定义自己的ADT时,应始终将根密封特征(或密封类)扩展Product with Serializable
。如果标准图书馆设计师遵循这一建议,我们会好得多。
答案 1 :(得分:2)
我认为如果你将类型归属添加到Seqs It&#39; ll编译的元素:
Seq(Left(1): Either[Int, String], Right(""): Either[Int, String]).toJson
Seq(Left(1): Either[Int, String], Right(""): Either[Int, String]).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat))
你也可以给它一个类型的归属:
(Seq(Left(1), Right("")): Either[Int, String]).toJson
(Seq(Left(1), Right("")): Either[Int, String]).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat))
我认为问题在于scalac试图确定您为Seq提供的元素之间的共同最小上界,以推导出单一类型(因为标准集合需要为其元素提供同类数据类型)和它没有给予帮助,也无法推断出你想要的东西。如果scala标准库已经将带有Serializable的扩展产品扩展到抽象类这两种定义你都不需要这样做,但由于子类型Right和Left都是case类(隐式扩展Product和Serializable),包含在推断类型中,这会导致喷涂所需的不变类型出现问题。