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
转换为MyDouble
class,它有~=
方法。
问题:
但是,为什么下面的代码无需定义 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])]
,编译器将搜索以下对象的伴生对象:Foo
、Tuple2
、A
、B
和 C
- 正如我们的实验所示,嵌套类型也很重要 -
A.B.C
将触发 A
、B
和 C
中的查找
所以在你的情况下,Scala 会在同伴中寻找隐含的:
- 在
MyEpsilon
的情况下:在 MyEpsilon
和在 Implicits
- 在
Double
的情况下:在 Double
这就是为什么您的第一个示例可以编译而第二个示例无法找到隐式的原因。
我正在尝试在 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
转换为MyDouble
class,它有~=
方法。
问题:
但是,为什么下面的代码无需定义 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])]
,编译器将搜索以下对象的伴生对象:Foo
、Tuple2
、A
、B
和C
- 正如我们的实验所示,嵌套类型也很重要 -
A.B.C
将触发A
、B
和C
中的查找
- 例如如果您有
所以在你的情况下,Scala 会在同伴中寻找隐含的:
- 在
MyEpsilon
的情况下:在MyEpsilon
和在Implicits
- 在
Double
的情况下:在Double
这就是为什么您的第一个示例可以编译而第二个示例无法找到隐式的原因。