Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Reads is not declared covariant?

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

like image 917
phadej Avatar asked Aug 29 '14 11:08

phadej


2 Answers

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.

like image 190
Travis Brown Avatar answered Nov 07 '22 02:11

Travis Brown


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".

like image 3
Paul Draper Avatar answered Nov 07 '22 02:11

Paul Draper