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

发布于 2024-10-16 22:42:29 字数 3252 浏览 3 评论 0原文

考虑以下简单代码来创建类型安全等于。第一部分允许我为任何类型创建一个 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 类型>、AnyValAny。不管怎样,我进一步探索了一些,假设这个问题与协方差有关。我更改了 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. 1是一个Int;通过范围内的隐式,我可以创建一个 Identity[Int] ,然后使用 === 方法。但这不起作用,因为参数不是 Int。再次尝试考虑 1 的替代类型
  2. 。1 是 AnyVal;通过范围内的隐式,我可以创建一个 Identity[AnyVal],然后使用 ===。但这不起作用,因为虽然参数是 AnyVal,但范围内没有隐式 Equals[AnyVal]。再次尝试考虑 1 的替代类型
  3. 。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. 1 is an Int; with the implicits in scope I can create an Identity[Int] and then use the === method. But this does not work because the argument is not an Int. Try again considering the alternate types for 1.
  2. 1 is an AnyVal; with the implicits in scope, I can create an Identity[AnyVal] and then use ===. But this does not work because, although the argument is an AnyVal, there is no implicit Equals[AnyVal] in scope. Try again considering the alternate types for 1.
  3. 1 is an Any; with the implicits in scope, I can create an Identity[Any] and then use ===. This works because both the argument is an Any and there is an Equals[Any] in scope.

Why does the type-inference only consider the strictest type of 1 (i.e. Int)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ま柒月 2024-10-23 22:42:29

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

根据语言规范(pdf),第 7.2 节:

如果有多个符合隐式参数的参数
参数的类型,将使用规则选择最具体的一个
静态重载解析(§6.26.3)。

这也是支撑 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:

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文