在查看隐式转换时,为什么类型推断仅选择最具体类型的目标引用?

时间:2011-02-10 12:11:17

标签: scala type-inference typeclass

考虑以下简单代码来创建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类型的引用在逻辑上具有IntAnyValAny类型。无论如何,我探讨了一点,假设问题与协方差有关。我更改了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没有问这个问题(对于我的第一个例子):

  1. 1是Int;根据范围中的含义,我可以创建Identity[Int],然后使用===方法。但这不起作用,因为参数不是Int。请再次考虑1的替代类型。
  2. 1是AnyVal;根据范围内的含义,我可以创建Identity[AnyVal],然后使用===。但这不起作用,因为虽然参数是AnyVal,但范围内没有隐式Equals[AnyVal]。请再次考虑1的替代类型。
  3. 1是Any;根据范围内的含义,我可以创建Identity[Any],然后使用===。这是有效的,因为参数都是Any,范围内有Equals[Any]
  4. 为什么类型推断只考虑最严格的1(即Int)?

1 个答案:

答案 0 :(得分:6)

您在这里看到的是优先级隐式转换,在Scala 2.8中添加。

根据Language Specification (pdf),部分 7.2

  

如果有几个符合条件的参数与隐式匹配   参数的类型,将使用规则选择最具体的类型   静态重载决策(第6.26.3节)。

这也是支持2.8 collectionsCanBuildFrom行为的机制。