【问题标题】:Ambiguous implicit values模棱两可的隐含值
【发布时间】:2014-08-21 16:58:50
【问题描述】:

直到最近遇到奇怪的问题,我一直认为我理解 scala 的隐含。

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

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,但这个简化版足以重现错误。

【问题讨论】:

    标签: scala implicit-conversion ambiguity


    【解决方案1】:

    您在示例中遇到的模棱两可之处在于您没有告诉 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 都定义了一个,它使用哪个隐式?我敢肯定那里还有更多可怕的角落案例,但是这个跳出来给我。

    【讨论】:

    • 你说得对,它解决了问题,但想象convert 是隐含的,f 定义为def f(s: String): Foo = s。是不可能实现的吗?理论上,scala 可以从f 的返回类型推断出Foo 类型,那么为什么会报错呢?
    • @lambdas 可以吗? Foo 在那个位置上实际上是 +Foo,所以理论上,如果 f 作为函数传递给一些 def bar(f: String => Foo),那么任何扩展 Foo 的东西也可以。那么你的convert[A] 总是会尝试推断Foo?即使你传入了def baz[A <: Foo](s: String): A 函数?我不知道。这些类型的事情让我夜不能寐。
    【解决方案2】:

    问题是您正在使泛型类型 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

    【讨论】:

      猜你喜欢
      • 2018-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 1970-01-01
      • 2014-01-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多