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


为什么玩-json Reads 特征未被声明为协变:

trait Reads[+A] 

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

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

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


7500
2017-08-29 11:40


起源



答案:


它可能是协变的,尤其是当您查看时 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”。


2
2017-10-04 01:56



谢谢,正如我想的那样,隐含的情况就是如此。你能评论一下这个要点吗?如果有人写的话,你有什么看法? reads[Child].map(x => x) 要么 reads[Child].as[Reads[Parent]],后者会发生什么错误吗? - phadej
@phadej,你的意思是 .asInstanceOf?无论如何,我不认为扩展类型有什么不好。唯一的问题是它是否自动完成了implicits。 - Paul Draper


答案:


它可能是协变的,尤其是当您查看时 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”。


2
2017-10-04 01:56



谢谢,正如我想的那样,隐含的情况就是如此。你能评论一下这个要点吗?如果有人写的话,你有什么看法? reads[Child].map(x => x) 要么 reads[Child].as[Reads[Parent]],后者会发生什么错误吗? - phadej
@phadej,你的意思是 .asInstanceOf?无论如何,我不认为扩展类型有什么不好。唯一的问题是它是否自动完成了implicits。 - Paul Draper


假设 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 不是逆变的。


8
2017-08-29 11:58



但如果我有非隐含的 readsBar: Reads[Bar],和 readsBaz: Reads[Baz]: 然后 readsFoo = readsBar orElse readsBaz 不起作用。然而 readsFoo = readsBar.map(x => x) orElse readsBaz.map(x => x) 没关系。在这里,我看到协变地图。 - phadej
这仍然困扰着我,我写了一个小小的要点,希望澄清问题: gist.github.com/phadej/c60912802dc494c3212b - phadej
我不是这个解释就足够了。您仍然可以将JSON转换为 Foo那么这样, Reads[Bar]  是 一个 Reads[Foo]。 - Paul Draper
使阅读逆变是没有任何意义的。这意味着Reads [Foo]也是Reads [Bar]和Reads [Baz]。 - Matthias Berndt