模糊的隐含值

时间:2014-08-21 16:58:50

标签: scala implicit-conversion ambiguity

我一直以为我理解scala implicits直到最近才遇到奇怪的问题。

在我的应用程序中,我有几个域类

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

一个能够从字符串构造域对象的类。它可以被子类化以进行真正的反序列化它并不重要。

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

接下来,有隐式反序列化器

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

将字符串转换为域类之一的帮助器

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

不幸的是,在尝试使用它时

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

我遇到像

这样的编译器错误
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)
                                      ^

对我而言,代码似乎很简单。 Reads[Foo]Reads[Bar]是一个完全不同的类型,它有什么含糊不清的地方?

真正的代码要复杂得多并使用play.api.libs.json,但这个简化版本足以重现错误。

2 个答案:

答案 0 :(得分:13)

您在示例中遇到的歧义是您没有告诉Scalac您想要使用哪一个。您需要用

替换代码
def f(s: String): Foo = convert[Foo](s)

为了弄清楚要使用哪一个。它不能从f的返回类型推断出来。它需要在这里明确。

对评论的回应

让我在这里扮演魔鬼的拥护者。

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

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

假设为BarBaz定义了一个隐式,它会使用隐式吗?我确定那里有更多恶魔般的角落案件,但是这个案件跳出来了。

答案 1 :(得分:8)

问题是您正在制作通用类型A不变量。相反,这应该是协变的。所以这应该按如下方式实施

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

// This `+` ↓ is the only difference  
class Reads[+A] {
  def read(s: String): A = throw new Exception("not implemented")
}

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

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

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

有关详细信息,请参阅http://docs.scala-lang.org/tutorials/tour/variances.html