scalatypeclassimplicits

Implicit lookup for Typeclass of None is not compatible with Contravariant Typeclass of Option


I don't get the following code to compile and I am curious of what I did wrong.

I defined a Contravariant Jsonwriter Trait and a function accepting implicit writers:

trait JsonWriter[-A] {
  def write(value: A): Json
}

object Json {
  def toJson[A](value: A)(implicit writer: JsonWriter[A]): Json =
  writer.write(value)
}

Additionally I have defined some instances of these writers:

object JsonWriterInstances {
  implicit val stringWriter: JsonWriter[String] =
    (value: String) => JsString(value)

  implicit val doubleWriter: JsonWriter[Double] =
    (value: Double) => JsNumber(value)

  class OptionWriter[-T](writer: JsonWriter[T]) extends JsonWriter[Option[T]] {
    def write(value: Option[T]): Json = {
        value match {
          case None    => JsNull
          case Some(x) => writer.write(x)
        }
      }
  }
  implicit def optionWriter[T](implicit writer: JsonWriter[T]):
      JsonWriter[Option[T]] = new OptionWriter[T](writer)

}

Now I have written a test:

"write double Option" in {
  Some(1.0).toJson should be(JsNumber(1.0))
  None.toJson should be(JsNull)
}

The first test for Some(1.0) works fine The second one for None throws:

Error:(40, 12) could not find implicit value for parameter writer: JsonWriter[None.type]
    None.toJson should be(JsNull)

If you want to try the code my JsonType definitions for this example are:

sealed trait Json

final case class JsObject(get: Map[String, Json]) extends Json

final case class JsString(get: String) extends Json

final case class JsNumber(get: Double) extends Json

case object JsNull extends Json

Solution

  • None, if you dont say anything else, is a Option[Nothing], so OptionWriter[Nothing] needs a JsonWriter[Nothing]. If you try Json.toJson(Some(1)) is the same, there is no JsonWriter[Int].

    On the other hand, Json.toJson(None:Option[String]) works, because OptionWriter[String] can get a JsonWriter[String].