score:1

Accepted answer

It seems like there is an extra complexity from a mix of different types of polymorphism in both examples. Here is a minimal example of just a type class:

// type class itself
trait JsonParser[T] {
  def parse(payload: String): Try[T]
}

// type class instances
object JsonParserInstances {
  implicit val type1Parser: JsonParser[Type1] = new JsonParser[Type1] {
    def parse(payload: String): Try[Type1] = ???
  }

  implicit val type2Parser: JsonParser[Type2] = new JsonParser[Type2] {
    def parse(payload: String): Try[Type2] = ???
  }
}

// type class interface
object JsonInterface {
  def parse[T](payload: String)(implicit T: JsonParser[T]): Try[T] = {
    T.parse(payload)
  }
}

def main(args: Array[String]): Unit = {
  import JsonParserInstances._
  JsonInterface.parse[Type1]("3")
  JsonInterface.parse[Type2]("3")
}

More info:

score:4

As I've already tried to explain in the comments, the problem is that the method

override def parse[T](payload: String): Try[T] = parseInternal(payload)

does not accept any JsonParserLike[T] instances. Therefore, the compiler has no way to insert the right instance of JsonParserLike[T] at the call site (where the type T is known).

To make it work, one would have to add some kind of token that uniquely identifies type T to the argument list of parse. One crude way would be to add a JsonParserLike[T] itself:

import util.Try

trait Parser {
  def parse[T: JsonParserLike](payload : String) : Try[T]
}

class JsonParser extends Parser {

  override def parse[T: JsonParserLike](payload: String): Try[T] = 
    parseInternal(payload)

  private def parseInternal[T:JsonParserLike](payload:String):Try[T] = {
    implicitly[JsonParserLike[T]].parse(payload)
  }
} 

trait JsonParserLike[T] {
  def parse(payload: String): Try[T]
}

object JsonParserLike {
  implicit val type1Parser: JsonParserLike[String] = ???
  implicit val type2Parser: JsonParserLike[Int] = ???
}

Now it compiles, because the JsonParserLike[T] required by parseInternal is inserted automatically as an implicit parameter to parse.

This might be not exactly what you want, because it creates a hard dependency between Parser interface and the JsonParserLike typeclass. You might want to get some inspiration from something like shapeless.Typeable to get rid of the JsonParserLike in the Parser interface, or just rely on circe right away.


Related Query

More Query from same tag