Scala import 语句错误地导入隐式值

Scala import statement incorrectly imports implicit value

我正在尝试在 Scala 中将 ~= operator 添加到 Double。以下是我的尝试:

package mypackage

object Implicits {

  case class MyEpsilon(value: Double) // Wrapper class for implicit argument

  implicit class MyDouble(val v: Double) extends AnyVal {
    /**
     * Return true if 2 doubles are approximately equal
     */
    def ~=(other: Double)(implicit epsilon: MyEpsilon): Boolean = {
      scala.math.abs(v - other) <= epsilon.value
    }

    /**
     * The same as above, but implicit value type is Double
     */
    def ~~=(other: Double)(implicit epsilon: Double): Boolean = {
      scala.math.abs(v - other) <= epsilon
    }
  }

  implicit val defaultDouble: Double = 1e-6
  implicit val defaultMyEpsilon: MyEpsilon = MyEpsilon(1e-6)

}

所以当我们写1.0 ~= 1.01时,Scala应该隐式地将1.0转换为MyDoubleclass,它有~=方法。

问题: 但是,为什么下面的代码无需定义 MyEpsilon 类型的隐式值就可以为 ~= 工作? (另一方面,正如预期的那样,~~= 不起作用,因为我们需要定义类型为 Double 的本地隐式值。)

import mypackage.Implicits.MyDouble // Try to import only "MyDouble" class

val a: Double = 1.0
val b: Double = 1.0 + 1e-3

val res0 = (a ~= b) // Why does this work without having to define implicit value of type "MyEpsilon"?

// Below doesn't work unless we define a local implicit value of type "Double"
// error: could not find implicit value for parameter epsilon: Double
val res1 = (a ~~= b)

让我们在 REPL 中做一个实验:

@ object A {
    trait B
    object B {
      trait C
      object C {
        implicit val b: B = new B {}
      }
      implicit val c: C = new C {}
    }
  } 
defined object A

@ implicitly[A.B.C] 
res3: A.B.C = ammonite.$sess.cmd2$A$B$$anon@13f9ad9

@ implicitly[A.B] 
cmd4.sc:1: could not find implicit value for parameter e: ammonite.$sess.cmd2.A.B
val res4 = implicitly[A.B]
                     ^
Compilation Failed

我们可以看到:

  • 发现 A.B 中类型 A.B.C 的隐式
  • 未找到 A.B.C 中类型 A.B 的隐式

当 Scala 开始寻找隐式时,它或多或少是这样工作的:

  • 查看当前作用域,是否有可见类型的值?如果是这样,它们是否带有隐式注释? (这也意味着当你用 val foo: Foo 覆盖 implicit val foo: Foo 时,隐式从隐式范围中消失)
  • 如果有 none 查看对所寻找类型有贡献的所有类型的伴生对象
    • 例如如果您有 Foo[(A, B[C])],编译器将搜索以下对象的伴生对象:FooTuple2ABC
    • 正如我们的实验所示,嵌套类型也很重要 - A.B.C 将触发 ABC
    • 中的查找

所以在你的情况下,Scala 会在同伴中寻找隐含的:

  • MyEpsilon 的情况下:在 MyEpsilon 和在 Implicits
  • Double 的情况下:在 Double

这就是为什么您的第一个示例可以编译而第二个示例无法找到隐式的原因。