为什么读取不被声明为协变?

pha*_*dej 11 scala playframework-2.3

为什么play-json Readstrait没有被声明为covariant:

trait Reads[+A] 
Run Code Online (Sandbox Code Playgroud)

相关要点:https://gist.github.com/robertberry/9410272

协方差/逆变是否会干扰隐含?

或者,如何Reads为密封特征编写实例?https://gist.github.com/phadej/c60912802dc494c3212b

Tra*_*own 8

假设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
Run Code Online (Sandbox Code Playgroud)

以及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 _)
Run Code Online (Sandbox Code Playgroud)

但是Bar <: Foo,所以Reads[Bar] <: Reads[Foo],这没有任何意义 - 我没有说过如何解码a Baz,所以我显然实际上并没有Reads[Foo].

一个更好的问题可能是为什么Reads不是逆变.

  • 让 Reads 逆变是没有任何意义的。这意味着 Reads[Foo] 也是 Reads[Bar] 和 Reads[Baz]。 (2认同)

Pau*_*per 3

它可能是协变的,尤其是当您将其视为Reads[A]更丰富的形式时JsValue => A

但是……含蓄

Reads[A]不仅仅是从到转换的一种方式,它就是方式JsValueA

所以如果我们有

sealed trait Foo
case class Bar(a: Int)
case class Baz(b: Int)
Run Code Online (Sandbox Code Playgroud)

如果您定义了 a 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]
}
Run Code Online (Sandbox Code Playgroud)

Baz.blah和发生了什么Main.blah?前者使用Baz.reads,后者使用Foo.reads,因为(复杂的)隐式解析顺序。

这是一个边缘情况,我仍然认为你可以为协方差提出一个很好的论据,但它确实显示了“可以将 JSON 解析为Foo”和“可以将 JSON 解析为所有可能的东西Foo”之间的区别。