Why does type inference chooses only most specific type of the target reference when looking at implicit conversions?
Consider the following simple code to create a typesafe equals. This first section allows me to create an Identity
typeclass for any type.
scala> trait Equals[A] { def equal(a1 : A, a2 : A) : Boolean }
defined trait Equals
scala> sealed trait Identity[A] {
| def value : A
| def ===(b : A)(implicit e : Equals[A]) = e.equal(value, b)
| }
defined trait Identity
scala> implicit def ToIdentity[A](a : A) = new Identity[A] { val value = a }
ToIdentity: [A](a: A)java.lang.Object with Identity[A]
So, if I create a typeclass for Equals[Int]
, I should now be able to use my typesafe equals:
scala> implicit val EqualsInt = new Equals[Int] { def equal(i1 : Int, i2 : Int) = i1 == i2 }
EqualsInt: java.lang.Object with Equals[Int] = $anon$1@7e199049
scala> 1 === 2
res1: Boolean = false
scala> 1 === 1
res2: Boolean = true
scala> 1 === 1D
<console>:10: error: type mismatch;
found : Double(1.0)
required: Int
1 === 1D
^
OK, so far so good. What happens if I now create an Equals[Any]
?
scala> implicit val EqualsAny = new Equals[Any] { def equal(a1 :开发者_StackOverflow社区 Any, a2 : Any) = a1 == a2 }
EqualsAny: java.lang.Object with Equals[Any] = $anon$1@141d19
scala> 1 === 1D
<console>:11: error: type mismatch;
found : Double(1.0)
required: Int
1 === 1D
^
But then if I tell the compiler that my type is an Any
, rather than an Int
...
scala> (1 : Any) === 1D
res6: Boolean = true
So my question is "why is the compiler not considering all of the types which 1 logically has?"
That is, my understanding was that a reference of type Int
logically has the types Int
, AnyVal
and Any
. Anyway, I explored a little more, assuming the issue was something to do with covariance. I changed my definition of Identity
:
scala> sealed trait Identity[+A] {
| def value : A
| def ===[B >: A : Equals](b : B) = implicitly[Equals[B]].equal(value, b)
| }
defined trait Identity
This time I got the error:
scala> 1 === 1D
<console>:10: error: could not find implicit value for evidence parameter of type Equals[AnyVal]
1 === 1D
^
So if I create an Equals[AnyVal]
, then this also works:
scala> implicit val EqualsAnyVal = new Equals[AnyVal] { def equal(a1 : AnyVal, a2 : AnyVal) = a1 == a2 }
EqualsAnyVal: java.lang.Object with Equals[AnyVal] = $anon$1@67ce08c7
scala> 1 === 1D
res4: Boolean = true
So here I assume the issue is with the non-contravariance of Equals
. So I try again (but without creating an Equals[AnyVal]
):
scala> trait Equals[-A] { def equal(a1 : A, a2 : A) : Boolean }
defined trait Equals
scala> 1 === 1D
res3: Boolean = true
So, I can sort of see what the typer is doing here. But my question looks like this: why is the typer not asking the question (for my first example):
- 1 is an
Int
; with the implicits in scope I can create anIdentity[Int]
and then use the===
method. But this does not work because the argument is not anInt
. Try again considering the alternate types for 1. - 1 is an
AnyVal
; with the implicits in scope, I can create anIdentity[AnyVal]
and then use===
. But this does not work because, although the argument is anAnyVal
, there is no implicitEquals[AnyVal]
in scope. Try again considering the alternate types for 1. - 1 is an
Any
; with the implicits in scope, I can create anIdentity[Any]
and then use===
. This works because both the argument is anAny
and there is anEquals[Any]
in scope.
Why does the type-inference only consider the strictest type of 1 (i.e. Int)?
What you're seeing here is prioritized implicit conversion, added in Scala 2.8.
As per the Language Specification (pdf), Section 7.2:
If there are several eligible arguments which match the implicit parameter’s type, a most specific one will be chosen using the rules of static overloading resolution (§6.26.3).
This is also the mechanism that underpins CanBuildFrom
behaviour in 2.8 collections.
精彩评论