【问题标题】:Why Reads is not declared covariant?为什么 Reads 没有被声明为协变的?
【发布时间】: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

【问题讨论】:

    标签: scala playframework-2.3


    【解决方案1】:

    假设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) 没问题。在这里我看到了协变图。
    • 这仍然困扰着我,我写了一个小要点希望能澄清问题:gist.github.com/phadej/c60912802dc494c3212b
    • 我觉得这个解释还不够。您仍然有办法将 JSON 转换为 Foo,因此,Reads[Bar] Reads[Foo]
    • 使 Reads 逆变没有任何意义。这意味着 Reads[Foo] 也是 Reads[Bar] 和 Reads[Baz]。
    【解决方案2】:

    它可能是协变的,尤其是当您将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.blahMain.blah 发生了什么?前者使用Baz.reads,后者使用Foo.reads,因为(复杂的)隐式解析顺序。

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

    【讨论】:

    • 谢谢,正如我所想的那样,隐含就是这种情况。你能评论一下要点吗?你怎么看,写reads[Child].map(x => x)reads[Child].as[Reads[Parent]],后者会不会出问题?
    • @phadej,你的意思是.asInstanceOf?无论如何,我认为扩展类型没有什么不好。唯一的问题是它是否是自动为隐式完成的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-13
    • 2012-11-23
    相关资源
    最近更新 更多