对重载定义的模糊引用 - 一对二参数

Ant*_*oly 16 scala overloading variadic-functions

给定以下具有重载版本的伴随对象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))
}
Run Code Online (Sandbox Code Playgroud)

和两个实例

List(1) // Error - Ambiguity 
List('a', 'b') // Works fine
Run Code Online (Sandbox Code Playgroud)

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

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

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

gou*_*ama 7

要回答您的问题,我们需要了解Scala编译器必须执行重载分辨率时会发生什么.这在SLS 6.23.3(针对Scala 2.9)中有所描述.

让我们看一个稍微简单的例子:

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
}
Run Code Online (Sandbox Code Playgroud)

看看这三个电话:

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"
Run Code Online (Sandbox Code Playgroud)

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

现在它有一个1类型的参数,Nothing并找到适用于它的所有替代方法.它找到了两个:

  • apply[T](x1: T):它接受一个无界类型的参数,因此它可以接收一个类型的参数Nothing,
  • apply[T](elems: T*):它可以应用于0同一个无界类型的任何数量(包含)的参数,因此它可以接收单个类型的元素Nothing.

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

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

然后,编译器计算relative weight彼此剩余的每个备选方案.SLS说明了这一点

备选A相对于备选B 的相对权重是从0到2的数字,定义为总和

  • 如果A与B具体相同,则为1,否则为0
  • 1如果A在类或对象中定义,该类或对象派生自定义B的类或对象,否则为0.

此时,必须有一个选项比其他选项具有更高的分数,或者存在歧义错误.我们可以忽略"已定义"部分,它们在同一个地方定义.

  • A是特定的C因为你可以随时调用C单个参数A,
  • CA因为类型推断是特定的:你总是可以A使用参数调用C因为A可以采取任何东西(它的参数的类型可以推断为我们想要的任何东西).C作为一个的参数被认为是Seq[A]这样T被推断为Seq[A]A和它可以调用它.所以C具体如此A.

如果你A改为apply[T <: Int](x: T):可以看到它:它一直寻找最具体的一个,但这次类型推断无法找到A适用于C参数(a Seq)的方法,因为它不是一个子类型Int,所以A最具体.很显然,如果你改变了同样的事情发生Aapply(x: Int),类型推断甚至不能做任何事情.

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

最后,应用相同的逻辑可以解释为什么Test(1,2)工作正常:

  • B是具体的C:你可以随时拨打CB的论点,
  • C具体为B:无类型推断的量将管理以适应单个Seq成采用一个方法2个参数.

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

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

def apply[T](x1: T)
def apply[T](x: T*)
Run Code Online (Sandbox Code Playgroud)

要么

def apply[T](x1: Int, x2:T)
def apply[T](x1: Int, x: T*)
Run Code Online (Sandbox Code Playgroud)

等等...

编辑:我一开始并不确定重复参数是否被视为a Seq[A]或者TupleX[...]在寻找特异性时.它绝对不是一个元组,自动元组与任何这一点都无关.