Ambiguous implicit values

lambdas picture lambdas · Aug 21, 2014 · Viewed 13k times · Source

I've been thinking I understand scala implicits until recently faced strange problem.

In my application I have several domain classes

case class Foo(baz: String)
case class Bar(baz: String)

And a class that is able to construct domain object from a string. It could be subclassed to do real deserialization it doesn't matter.

class Reads[A] {
  def read(s: String): A = throw new Exception("not implemented")
}

Next, there are implicit deserializers

implicit val fooReads = new Reads[Foo]
implicit val barReads = new Reads[Bar]

And a helper to convert strings to one of domain classes

def convert[A](s: String)(implicit reads: Reads[A]): A = reads.read(s)

Unfortunatelly, when trying to use it

def f(s: String): Foo = convert(s)

I get compiler errors like

error: ambiguous implicit values:
 both value fooReads of type => Reads[Foo]
 and value barReads of type => Reads[Bar]
 match expected type Reads[A]
       def f(s: String): Foo = convert(s)
                                      ^

To me code seems simple and right. Reads[Foo] and Reads[Bar] is a completely different types, what is ambiguous about it?

The real code is much more complicated and uses play.api.libs.json but this simplified version is sufficient to reproduce the error.

Answer

wheaties picture wheaties · Aug 21, 2014

The ambiguity you're encountering in your example is that you didn't tell Scalac which one you wanted to use. You need to replace your code with

def f(s: String): Foo = convert[Foo](s)

in order for it to figure out which one to use. It can't infer from the return type of f. It needs to be explicit here.

Response to comment

Let me play devil's advocate here.

trait Foo
case class Bar(s: String) extends Foo
case class Baz(s: String) extends Foo

def f(s: String): Foo = convert(s)

which implicit does it use assuming there is one defined for both Bar and Baz? I'm sure there's more devilish corner cases out there but this one jumps out to me.