【发布时间】:2014-10-23 10:10:25
【问题描述】:
为什么 play-json Reads trait 没有被声明为协变:
trait Reads[+A]
相关要点:https://gist.github.com/robertberry/9410272
协变/逆变会干扰隐式吗?
或者可选地,如何为密封特征编写Reads 实例? https://gist.github.com/phadej/c60912802dc494c3212b
【问题讨论】:
为什么 play-json Reads trait 没有被声明为协变:
trait Reads[+A]
相关要点:https://gist.github.com/robertberry/9410272
协变/逆变会干扰隐式吗?
或者可选地,如何为密封特征编写Reads 实例? https://gist.github.com/phadej/c60912802dc494c3212b
【问题讨论】:
假设Reads 是协变的。我有一个简单的类型层次结构:
sealed trait Foo { def name: String }
case class Bar(name: String, i: Int) extends Foo
case class Baz(name: String, c: Char) extends Foo
还有一个案例类的Reads 实例:
import play.api.libs.functional.syntax._
import play.api.libs.json._
implicit val readsBar: Reads[Bar] = (
(__ \ 'name).read[String] and (__ \ 'i).read[Int]
)(Bar.apply _)
但是Bar <: Foo,所以Reads[Bar] <: Reads[Foo],这没有任何意义——我没有说过如何解码Baz,所以我显然没有Reads[Foo]。
一个更好的问题可能是为什么Reads 不是逆变的。
【讨论】:
readsBar: Reads[Bar] 和readsBaz: Reads[Baz]: 那么readsFoo = readsBar orElse readsBaz 不起作用。不过readsFoo = readsBar.map(x => x) orElse readsBaz.map(x => x) 没问题。在这里我看到了协变图。
Foo,因此,Reads[Bar] 是 Reads[Foo]。
它可能是协变的,尤其是当您将Reads[A] 视为JsValue => A 的更丰富形式时。
但是....隐含性。
Reads[A] 不仅仅是将JsValue 转换为A 的一种 方式,它还是 方式。
如果我们有
sealed trait Foo
case class Bar(a: Int)
case class Baz(b: Int)
如果你定义了一个Reads[Bar],你也会(有协方差)有一个Reads[Foo]。
这可能有点奇怪。
object Foo {
implicit reads: Reads[Foo] =
implicitly[Reads[Bar]].orElse[implicitly[Reads[Baz]]]
}
object Bar {
implicit reads = Json.reads[Bar] // {"a":0}
}
object Baz {
implicit reads = Json.reads[Baz] // {"b":0}
def blah(jsValue: JsValue): Foo = jsValue.as[Foo]
}
object Main {
def blah(jsValue: JsValue): Foo = jsValue.as[Foo]
}
Baz.blah 和 Main.blah 发生了什么?前者使用Baz.reads,后者使用Foo.reads,因为(复杂的)隐式解析顺序。
这是一个边缘案例,我仍然认为你可以为协方差提出一个很好的论据,但它确实显示了“可以将 JSON 解析为 Foo”和“可以将 JSON 解析为所有可能的Foo"。
【讨论】:
reads[Child].map(x => x)或reads[Child].as[Reads[Parent]],后者会不会出问题?
.asInstanceOf?无论如何,我认为扩展类型没有什么不好。唯一的问题是它是否是自动为隐式完成的。