考虑以下简单代码来创建typesafe等号。第一部分允许我为任何类型创建Identity
类型类。
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]
所以,如果我为Equals[Int]
创建一个类型类,我现在应该可以使用我的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
^
好的,到目前为止一切顺利。如果我现在创建Equals[Any]
?会发生什么
scala> implicit val EqualsAny = new Equals[Any] { def equal(a1 : 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
^
但是如果我告诉编译器我的类型是Any
,而不是Int
......
scala> (1 : Any) === 1D
res6: Boolean = true
所以我的问题是“为什么编译器不考虑所有的逻辑上有哪些类型?”
也就是说,我的理解是Int
类型的引用在逻辑上具有Int
,AnyVal
和Any
类型。无论如何,我探讨了一点,假设问题与协方差有关。我更改了Identity
的定义:
scala> sealed trait Identity[+A] {
| def value : A
| def ===[B >: A : Equals](b : B) = implicitly[Equals[B]].equal(value, b)
| }
defined trait Identity
这次我收到了错误:
scala> 1 === 1D
<console>:10: error: could not find implicit value for evidence parameter of type Equals[AnyVal]
1 === 1D
^
因此,如果我创建Equals[AnyVal]
,那么这也有效:
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
所以在这里我假设问题是Equals
的非逆变性。所以我再试一次(但没有创建Equals[AnyVal]
):
scala> trait Equals[-A] { def equal(a1 : A, a2 : A) : Boolean }
defined trait Equals
scala> 1 === 1D
res3: Boolean = true
所以,我可以看看typer在这里做了些什么。但我的问题看起来像这样:为什么typer没有问这个问题(对于我的第一个例子):
Int
;根据范围中的含义,我可以创建Identity[Int]
,然后使用===
方法。但这不起作用,因为参数不是Int
。请再次考虑1的替代类型。AnyVal
;根据范围内的含义,我可以创建Identity[AnyVal]
,然后使用===
。但这不起作用,因为虽然参数是AnyVal
,但范围内没有隐式Equals[AnyVal]
。请再次考虑1的替代类型。Any
;根据范围内的含义,我可以创建Identity[Any]
,然后使用===
。这是有效的,因为参数都是Any
,范围内有Equals[Any]
。为什么类型推断只考虑最严格的1(即Int)?
答案 0 :(得分:6)
您在这里看到的是优先级隐式转换,在Scala 2.8中添加。
根据Language Specification (pdf),部分 7.2 :
如果有几个符合条件的参数与隐式匹配 参数的类型,将使用规则选择最具体的类型 静态重载决策(第6.26.3节)。
这也是支持2.8 collections中CanBuildFrom
行为的机制。