scala 对重载定义的歧义引用 - 一个对两个参数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16829114/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 05:20:34  来源:igfitidea点击:

Ambiguous Reference to overloaded definition - One vs Two Parameters

scalaoverloadingvariadic-functions

提问by Anthony Accioly

Given the following companion object with overloaded versions of apply:

给定以下带有重载版本的伴随对象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))
}

And the two instantiations

和两个实例

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

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

scalac 抱怨第一次实例化(对重载定义的引用不明确),因为单个参数和 varargs 方法同样具体

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).

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

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 specificthan the varargs method where the single argument method isn't?

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

采纳答案by gourlaysama

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).

要回答您的问题,我们需要看看当 Scala 编译器必须执行重载解析时会发生什么。这在 SLS 6.23.3(对于 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
}

And look at these three calls:

看看这三个调用:

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"

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

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

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

现在它只有一个参数1,类型为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 (0included) of arguments of the same unbounded type, so it can receive a single element of type Nothing.
  • apply[T](x1: T): 它接受一个无界类型的参数,所以它可以接收一个类型的参数Nothing
  • apply[T](elems: T*):它可以应用于任意数量(0包括)相同无界类型的参数,因此它可以接收单个类型的元素Nothing

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

如果只有一个,它会停在那里并选择那个。

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 1of 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.

第二步与上述相同,除了这次它使用未定义的预期类型键入每个参数。基本上在这里它查看剩下的两个替代方案,并找出它们是否适用于1type的参数A <: Int。没有运气,他们两个都是。如果你有两个变化apply[T](x1: T),以apply(x1: String)别去找另外一个,在这里将只有一个适用万般无奈之下,它会成功,并停止。

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

然后编译器计算relative weight彼此留下的每个备选方案的 。SLS 指出

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

  • 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.

备选方案 A相对备选方案 B的相对权重是一个从 0 到 2 的数字,定义为

  • 如果 A 与 B 一样具体,则为 1,否则为 0,并且
  • 1 如果 A 定义在从定义 B 的类或对象派生的类或对象中,否则为 0。

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.

此时,必须有一个选项的得分高于所有其他选项,否则存在歧义错误。我们可以忽略“定义”部分,它们是在同一个地方定义的。

  • Ais as specific as Cbecause you can always call Cwith the single argument of A,
  • Cis as specific as Abecause of type inference: you can always call Awith the argument of Csince Acan take anything (its parameter's type can be inferred to whatever we want). C's parameters is seen as a Seq[A]so Tis inferred as Seq[A]in Aand it can call it. So Cis as specific as A.
  • A很具体,C因为您始终可以C使用 的单个参数进行调用A
  • CA与类型推断一样具体:您始终可以A使用参数进行调用,C因为A可以接受任何内容(其参数的类型可以推断为我们想要的任何内容)。C的参数被视为一个Seq[A]soT被推断为Seq[A]inA并且它可以调用它。所以C是具体的A

This can be seen if you change Ato 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 Aapplicable to C's argument (a Seq) because it isn't a subtype of Int, so Ais the most specific. Obviously, the same thing happens if you change Ato apply(x: Int), type inference can't even do anything.

如果您更改A为 ,则可以看到apply[T <: Int](x: T)这一点:它一直在寻找最具体的一个,但是这次类型推断无法找到A适用于C的参数 (a Seq) 的方法,因为它不是 的子类型Int,所以A是最具体的。显然,如果您更改Aapply(x: Int),则会发生同样的事情,类型推断甚至无法执行任何操作。

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 Intand type inference cannot change it to fit C's argument anymore.

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

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

最后,应用相同的逻辑会告诉你为什么Test(1,2)工作正常:

  • Bis as specific as C: you can always call Cwith B's arguments,
  • but Cis notas specific as B: no amount of type inference will manage to fit a single Seqinto a method that takes twoparameters.
  • B具体如下C:您始终可以C使用B的参数调用,
  • C具体为B:无类型推断的量将管理以适应单个Seq成采用一个方法2个参数。

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

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

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:

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

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

Or

或者

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

And so on...

等等...

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.

编辑:起初我不确定在寻找特异性时重复参数是否被视为 aSeq[A]或 a TupleX[...]。它绝对不是元组,自动元组与这些无关。

回答by som-snytt

The fixed-arity method is always more specific than the var-arity.

Fixed-arity 方法总是比 var-arity 更具体。

f(P1, P2)doesn't apply to (a, b, c, ...), which is how you can think of f(P*).

f(P1, P2)不适用于(a, b, c, ...),这是您可以想到的f(P*)

Conversely, though, f(P*)takes the shape f(p1,..,pn)for purposes of applicability to N args. So it always applies and is not as specific as the fixed-arity method.

然而,相反,为了适用于 N args 的目的而f(P*)采用形状f(p1,..,pn)。所以它总是适用并且不像固定数量方法那么具体。

So that's the normal reason your f(a,b)is more specific than f(P*).

所以这就是你f(a,b)f(P*).

For the one-arg case, it depends on what you pick for the type param.

对于单参数情况,这取决于您为类型参数选择的内容。

f[A](a: A)does apply to (a, b, ...)by tupling and taking A as a Tuple.

f[A](a: A)确实适用于(a, b, ...)通过元组并将 A 作为元组。

By saying A = Int, then obviously A can't be taken as a Tuple.

通过说 A = Int,那么显然 A 不能被视为元组。

Sample confusion about var-arity and how affects specificity:

关于变异性以及如何影响特异性的示例混淆:

https://issues.scala-lang.org/browse/SI-4728

https://issues.scala-lang.org/browse/SI-4728

object Foo {
  def apply[T](): Int = 1
  def apply[T](x1: T): Int = 2
  def apply[T](x1: T, x2: T): Int = 3
  def apply[T](elems: T*): Int = 4

  // two or more
  def canonically[T](x1: T, x2: T, rest: T*): List[T] = ???
}

object Test extends App {
  Console println Foo(7)
  Console println Foo[Int](7)
  Console println Foo(7,8)
  Console println Foo[Pair[Int,Int]](7,8)
}

You might want to post this question on stackoverload.com, the site where the overloading specialists gather. Your browser may be redirected to overloading-is-evil.com.

您可能想在 stackoverload.com 上发布此问题,该站点是重载专家聚集的站点。您的浏览器可能会被重定向到overloading-is-evil.com。