对重载定义的模棱两可的引用-一对二参数 [英] Ambiguous Reference to overloaded definition - One vs Two Parameters

查看:78
本文介绍了对重载定义的模棱两可的引用-一对二参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为以下伴随对象提供apply的重载版本:

Given the following companion object with overloaded versions of apply:

object List {
  def apply[T](): List[T] = new Nil
  def apply[T](x1: T): List[T] = new Cons(x1, new Nil)
  def apply[T](x1: T, x2: T): List[T] = new Cons(x1, new Cons(x2, new Nil))
  def apply[T](elems: T*): List[T] = 
    elems.foldRight(List[T])((elem, l) => new Cons(elem, l))
}

还有两个实例

List(1) // Error - Ambiguity 
List('a', 'b') // Works fine

scalac抱怨第一个实例化(对重载定义的模糊引用),因为单个参数和varargs方法都是

scalac complains about the first instantiation (ambiguous reference to overloaded definition) because both the single argument and the varargs method are equally specific.

搜索stackoverflow我发现可以强制使用单参数方法. List[Int](1)将使编译器使用def apply[T](x1: T).

Searching stackoverflow I've found that it is possible to force the single argument method. List[Int](1) will make the compiler use def apply[T](x1: T).

我的问题是,为什么第二个实例匹配def apply[T](x1: T, x2: T)而没有额外的提示"?换句话说,为什么两个参数方法比不使用单个参数方法的varargs方法更具特异性?

My question is why does the second instantiation match def apply[T](x1: T, x2: T) without extra "hints"? In other words, why is the two argument method more specific than the varargs method where the single argument method isn't?

推荐答案

要回答您的问题,我们需要看看Scala编译器必须执行重载解析时会发生什么. SLS 6.23.3(针对Scala 2.9)对此进行了说明.

To answer your question we need to have a look at what happens when the Scala compiler has to perform overloading resolution. This is described in SLS 6.23.3 (for Scala 2.9).

让我们以您的示例稍微简单一些:

Let's take a slightly simpler version of your example:

object Test {
  def apply[T](x1: T) = "one arg"                      // A
  def apply[T](x1: T, x2: T) = "two args"              // B
  def apply[T](elems: T*) = "var-args: " + elems.size  // C
}

看看这三个电话:

Test(1) // fails, ambiguous reference, A and C both match arguments
Test[Int](1) // returns "one arg"
Test(1,2) // returns "two args", not "var-args: 2"

让我们从第一个开始.首先,编译器查看每个参数的 shape ,这是一种基本上描述该参数是值还是函数的类型.在这里,没有困难,1是一个非常正常的无聊值,其形状为Nothing类型.

Let's start with the first one. First, the compiler looks at the shape of each argument, which is a type that describes, basically, if the argument is a value or a function. Here, no difficulty, 1 is a very normal, boring value and its shape is the type Nothing.

现在,它具有类型为Nothing的单个参数1,并找到了适用于它的所有替代项.它找到其中两个:

Now it has a single argument 1, of type Nothing, and finds all alternatives that are applicable to it. It finds two of them:

  • apply[T](x1: T):它接受一个无界类型的参数,因此它可以接收类型为Nothing的参数,
  • apply[T](elems: T*):可以将其应用于任意数量(包括0)相同的无边界类型的参数,因此它可以接收类型为Nothing的单个元素.
  • apply[T](x1: T): it takes a single argument of unbounded type, so it can receive a argument of type Nothing,
  • apply[T](elems: T*): it can be applied to any number (0 included) of arguments of the same unbounded type, so it can receive a single element of type Nothing.

只有一个,它会停在那里并选择那个.

It there were only one, it would stop there and choose that one.

第二步与上面的步骤相同,只是这次它用不确定的预期类型键入每个参数.基本上,这里查看剩下的两个选择,并找出它们是否适用于类型为A <: Int的参数1.没运气,他们俩都是. 如果您是两个人,将apply[T](x1: T)更改为apply(x1: String),而将另一个保持不变,那么这里只剩下一个适用的替代方法,它将成功并停止.

The second step is identical to the above, except this time it types each argument with an undefined expected type. Basically here it looks at the two alternatives left and finds out if they are applicable to the argument 1 of type A <: Int. No luck, they both are. If you were two change apply[T](x1: T) to apply(x1: String) and leave the other one alone, here there would only be one applicable alternative left and it would succeed and stop.

然后,编译器将计算彼此剩下的每个替代项的relative weight. SLS指出

Then the compiler computes the relative weight of each alternative left over each other. The SLS states that

替代项A相对于替代项B的相对权重是一个从0到2的数字 定义为

The relative weight of an alternative A over an alternative B is a number from 0 to 2, defined as the sum of

  • 如果A与B一样具体,则为1,否则为0,并且
  • 1,如果在从类或对象派生的类或对象中定义了A 定义B,否则定义0.
  • 1 if A is as specific as B , 0 otherwise, and
  • 1 if A is defined in a class or object which is derived from the class or object defining B , 0 otherwise.

在这一点上,必须有一个得分比所有其他得分都高的替代方案,否则会有歧义性错误.我们可以忽略已定义"部分,它们是在同一位置定义的.

At this point, there must be one alternative that has a higher score than all others, or there is an ambiguity error. We can ignore the "defined" part, they are defined in the same place.

  • AC一样具体,因为您始终可以使用A的单个参数来调用C
  • CA一样具体,原因是类型推断:您总是可以使用C作为参数来调用A,因为A可以接受任何内容(其参数的类型可以推断为我们想要的任何类型). C的参数被视为Seq[A],因此在A中将T推断为Seq[A],并且可以调用它.因此CA一样具体.
  • A is as specific as C because you can always call C with the single argument of A,
  • C is as specific as A because of type inference: you can always call A with the argument of C since A can take anything (its parameter's type can be inferred to whatever we want). C's parameters is seen as a Seq[A] so T is inferred as Seq[A] in A and it can call it. So C is as specific as A.

如果将A更改为apply[T <: Int](x: T)可以看到:一直寻找最具体的一个,但是这次类型推断无法找到使A适用于C的方法.的参数(a Seq),因为它不是Int的子类型,所以A是最具体的.显然,如果将A更改为apply(x: Int),则类型推断同样会发生.

This can be seen if you change A to apply[T <: Int](x: T): it goes all the way to looking for the most specific one, but this time type inference cannot find a way to make A applicable to C's argument (a Seq) because it isn't a subtype of Int, so A is the most specific. Obviously, the same thing happens if you change A to apply(x: Int), type inference can't even do anything.

这也解释了为什么Test[Int](1)成功调用一个参数版本.最后两个备选方案是相同的,但是A的类型参数已绑定到Int,并且类型推断不能再将其更改为适合C的参数.

This also explains why Test[Int](1) succeeds in calling the one argument version. The two final alternatives are identical, but A's type parameter has been bound to Int and type inference cannot change it to fit C's argument anymore.

最后,应用相同的逻辑将为您提供Test(1,2)正常工作的原因:

Finally, applying the same logic gives you why Test(1,2) works fine:

  • BC一样具体:您始终可以使用B的参数调用C
  • 但是CB一样不是:没有任何类型的推断将设法使单个Seq适应需要 two 的方法参数.
  • B is as specific as C: you can always call C with B's arguments,
  • but C is not as specific as B: no amount of type inference will manage to fit a single Seq into a method that takes two parameters.

所以apply[T](x1: T, x2: T)是最具体的,没有错误.

So apply[T](x1: T, x2: T) is the most specific and there are no errors.

基本上,为了使var-arg和普通方法产生歧义,您需要具有相同数量的参数,并且需要一种方法来欺骗(至少)最后一个参数的类型推断:

Basically for a var-arg and a normal method to produce an ambiguity, you they need to have the same number of arguments and a way to trick type inference on (at least) the last argument:

def apply[T](x1: T)
def apply[T](x: T*)

def apply[T](x1: Int, x2:T)
def apply[T](x1: Int, x: T*)

依此类推...

编辑:首先,我不确定在查找特异性时是将重复参数视为Seq[A]还是TupleX[...].它绝对不是一个元组,并且自动补光与此无关.

Edit: I wasn't sure at first whether the repeated parameter was seen as a Seq[A] or a TupleX[...] when looking for specificity. It definitely isn't a tuple, and auto-tupling has nothing to do with any of this.

这篇关于对重载定义的模棱两可的引用-一对二参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆