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