我一直以为我理解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
,但这个简化版本足以重现错误。
答案 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)
假设为Bar
和Baz
定义了一个隐式,它会使用隐式吗?我确定那里有更多恶魔般的角落案件,但是这个案件跳出来了。
答案 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。