Scala import statement incorrectly imports implicit value
I am trying to add ~=
operator to Double
in Scala. The below is my attempt:
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)
}
So that when we write 1.0 ~= 1.01
, Scala should implicitly convert 1.0
to MyDouble
class, which has ~=
method.
Question:
But why does the following code work for ~=
without having to define an implicit value of type MyEpsilon
?
(On the other hand, as expected, ~~=
doesn't work, because we need to define a local implicit value of type 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)
Solution 1:
Let's make an experiment in 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$2@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
We can see that:
- implicit of type
A.B.C
placed inA.B
was found - implicit of type
A.B
placed inA.B.C
was not found
When Scala starts looking for implicits it works more or less like this:
- look at the current scope, are there any visible values of sought type? if so, are they annotated with implicit? (this also means that when you overshade
implicit val foo: Foo
withval foo: Foo
implicit disappears from implicit scope) - if there are none look at companion objects of all the types contributing to the sought type
- e.g. if you have
Foo[(A, B[C])]
, compiler will search companion objects of:Foo
,Tuple2
,A
,B
andC
- as our experiment shown nested types also count -
A.B.C
will trigger lookup inA
,B
andC
- e.g. if you have
So in your case Scala would look for implicit in companions:
- in case of
MyEpsilon
: inMyEpsilon
and inImplicits
- in case of
Double
: inDouble
That's why your first example compiles and the second fails to find the implicit.