scalatraitstypeclassimplicitscala-implicits

Ambiguous implicit values for Typeclass


I am trying to abstract out the json parsing logic that gets triggered for a specific type.

I started out creating a Parser trait as follows:

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

I have an implementation of this trait called JsonParser which is:

class JsonParser extends Parser {

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

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

The JsonParserLike is defined as follows:

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

object JsonParserLike {
  implicit val type1Parser:JsonParserLike[Type1] = new JsonParserLike[Type1] 
  {
    //json parsing logic for Type1
  }

  implicit val type2Parser:JsonParserLike[Type2] = new JsonParserLike[Type2]
  {
     //json parsing logic for Type2
  }
}

When I try compiling the above, the compilation fails with:

ambiguous implicit values:
[error]  both value type1Parse in object JsonParserLike of type => parser.jsonutil.JsonParserLike[parser.models.Type1]
[error]  and value type2Parser in object JsonParserLike of type => parser.jsonutil.JsonParserLike[parser.models.Type2]
[error]  match expected type parser.jsonutil.JsonParserLike[T]
[error]   override def parse[T](payload: String): Try[T] = parseInternal(payload)

Not sure why the implicit resolution is failing here. Is it because the parse method in the Parser trait doesn't have an argument of type parameter T?

I tried another approach as follows:

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

class JsonParser extends Parser {

  override def parse[T](payload: String): Try[T] = {
    import workflow.parser.JsonParserLike._
    parseInternal[T](payload)
  }

  private def parseInternal[U](payload:String)(implicit c:JsonParserLike[U]):Try[U] = {
     c.parse(payload)
  }
}

The above gives me the following error:

could not find implicit value for parameter c: parser.JsonParserLike[T]
[error]     parseInternal[T](payload)
[error]  

               ^

Edit: Adding the session from the REPL

scala> case class Type1(name: String)
defined class Type1

scala> case class Type2(name:String)
defined class Type2

scala> :paste
// Entering paste mode (ctrl-D to finish)

import scala.util.{Failure, Success, Try}

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

object JsonParserLike {

  implicit val type1Parser:JsonParserLike[Type1] = new JsonParserLike[Type1] {

    override def parse(payload: String): Try[Type1] = Success(Type1("type1"))
  }

  implicit val type2Parser:JsonParserLike[Type2] = new JsonParserLike[Type2] {

    override def parse(payload: String): Try[Type2] = Success(Type2("type2"))
  }
}

// Exiting paste mode, now interpreting.

import scala.util.{Failure, Success, Try}
defined trait JsonParserLike
defined object JsonParserLike

scala> :paste
// Entering paste mode (ctrl-D to finish)

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

class JsonParser extends Parser {

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

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

// Exiting paste mode, now interpreting.

<pastie>:24: error: ambiguous implicit values:
 both value type1Parser in object JsonParserLike of type => JsonParserLike[Type1]
 and value type2Parser in object JsonParserLike of type => JsonParserLike[Type2]
 match expected type JsonParserLike[T]
  override def parse[T](payload: String): Try[T] = parseInternal(payload)

Solution

  • 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: