Search code examples
scalatraitscontext-bound

In Scala, how to create a trait for classes with functions using different context bounds


Now I wanna serialize/deserialize Json data, and there are several json libraries to choose. However, they use different context-bounds for encoding/decoding, which make it hard to define a trait for them.

trait JsonLib {
  // def writes[T](data: T): String
  // def reads[T](jsonStr: String): Option[T]
}

object JsonCirce extends JsonLib {
  import io.circe.Encoder._
  import io.circe.Decoder._

  def writes[T: Encoder](data: T): String = ...
  def reads[T: Decoder](jsonStr: String): Option[T] =
}

//spray-json
object JsonSpray extends JsonLib {
  import spray.json._

  def writes[T: JsonWriter](data: T): String = ...

  def reads[T: JsonReader](jsonStr: String): Option[T] = ...
}

Is there a way to define the writes/reads in the trait?


Solution

  • You can generalize the type classes using higher kind types like:

    import scala.language.higherKinds
    import scala.util.Try
    import spray.json._
    import DefaultJsonProtocol._
    
    trait JsonLib[R[_], W[_]] {
      def writes[T: W](data: T): String
    
      def reads[T: R](jsonStr: String): Option[T]
    }
    
    //spray-json
    object JsonSpray extends JsonLib[JsonReader, JsonWriter] {
    
      override def writes[T: JsonWriter](data: T): String =
        data.toJson.compactPrint
    
      override def reads[T: JsonReader](jsonStr: String): Option[T] =
        Try(jsonStr.parseJson.convertTo[T]).toOption
    }
    
    // Test
    JsonSpray.writes(List(1, 2))
    JsonSpray.reads[List[Int]]("[1,2]")