Why play-json Reads trait is not declared covariant:
trait Reads[+A]
Related gist: https://gist.github.com/robertberry/9410272
Does covariance/contravariance interfere with implicits?
Or optionally, how to write Reads instance for sealed traits? https://gist.github.com/phadej/c60912802dc494c3212b
Suppose Reads were covariant. I've got a simple type hierarchy:
sealed trait Foo { def name: String }
case class Bar(name: String, i: Int) extends Foo
case class Baz(name: String, c: Char) extends Foo
And a Reads instance for one of the case classes:
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 _)
But Bar <: Foo, so Reads[Bar] <: Reads[Foo], and this makes no sense—I haven't said anything about how to decode a Baz, so I clearly don't actually have a Reads[Foo].
A better question might be why Reads isn't contravariant.
It could be covariant, especially when you view Reads[A] as just a richer form of JsValue => A.
But....implicitness.
Reads[A] is not only just a way to convert from JsValue to A, it is the way.
So if we had
sealed trait Foo
case class Bar(a: Int)
case class Baz(b: Int)
If you defined a Reads[Bar], you would also (with covariance) have a Reads[Foo].
This could be a bit weird.
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]
}
What is happening in Baz.blah and Main.blah? The former uses Baz.reads, and the latter uses Foo.reads, because of (complicated) implicit resolution order.
This is an edge case, and I still think you could make a good argument for covariance, but it does show the difference between "the thing can possibly parse JSON into Foo" and "the thing that can parse JSON into all possible Foo".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With