为什么类型推断在查看隐式转换时仅选择目标引用的最具体类型?
考虑以下简单代码来创建类型安全等于。第一部分允许我为任何类型创建一个 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]
创建一个类型类,我现在应该能够使用我的类型安全 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
那么我的问题是“为什么编译器不考虑 < 1 逻辑上具有的所有类型?”
也就是说,我的理解是 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
所以,我可以看到打字机在这里做什么。但是我的问题看起来像这样:为什么打字者不问问题(对于我的第一个例子):
- 1是一个
Int
;通过范围内的隐式,我可以创建一个Identity[Int]
,然后使用===
方法。但这不起作用,因为参数不是Int
。再次尝试考虑 1 的替代类型 - 。1 是
AnyVal
;通过范围内的隐式,我可以创建一个Identity[AnyVal]
,然后使用===
。但这不起作用,因为虽然参数是AnyVal
,但范围内没有隐式Equals[AnyVal]
。再次尝试考虑 1 的替代类型 - 。1 是
Any
;通过范围内的隐式,我可以创建一个Identity[Any]
,然后使用===
。这是有效的,因为两个参数都是Any
并且作用域中有一个Equals[Any]
。
为什么类型推断只考虑最严格的类型1(即Int)?
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 : 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)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您在这里看到的是 Scala 2.8 中添加的优先隐式转换。
根据语言规范(pdf),第 7.2 节:
这也是支撑 2.8 个集合。
What you're seeing here is prioritized implicit conversion, added in Scala 2.8.
As per the Language Specification (pdf), Section 7.2:
This is also the mechanism that underpins
CanBuildFrom
behaviour in 2.8 collections.