当方法以看似无关的方式重载时,为什么scala无法编译?

时间:2016-10-06 17:39:38

标签: scala overloading

class A {}
class B extends A {}

object Sample {
  def foo(a: Set[A]) {
    println("Hi Set[A]")
  }
  // def foo(a: String) {
  //   println("Hi A")
  // }
}

Sample.foo(Set(new B()))

以上代码与scala愉快地运行。但是,当我取消注释foo(a: String)时,代码无法编译:

test.scala:13: error: overloaded method value foo with alternatives:
  (a: String)Unit <and>
  (a: Set[this.A])Unit
 cannot be applied to (scala.collection.immutable.Set[this.B])
Sample.foo(Set(new B()))
       ^
one error found

foo(a: String)似乎与尝试使用foo调用Set[B]无关。发生了什么事?

编辑:

让我感到困惑的不仅仅是为什么未注释的版本无法编译,还有为什么编译,当foo(a: String)被注释掉时。我通过添加方法foo(a: String)来改变什么?

Set不变不解释当foo(a: String)被注释掉时成功编译的原因。

2 个答案:

答案 0 :(得分:3)

在工作用例中,Set.apply[T]的类型参数被推断为A,因为Set[A]是函数参数的预期类型。

重载没有预期类型的​​分辨率类型检查参数,因此编译器不能再使用Set[A]来指导您想要设置的内容。

这是the spec的一个重要内容,虽然现在它被更多关于地空通信的言论所掩盖。

  

否则,让Si ...成为通过键入每个类型获得的类型列表   论点如下。 [关于函数的东西。]所有其他参数   键入了未定义的预期类型。

如果它知道预期有Set[A],那么您的广告就会以Set[B]的方式输入。

您可以使用-Ytyper-debug观察输入决定,这会产生有时不易察觉的输出。

鉴于

class A ; class B extends A

object X { def f(as: Set[A]) = ??? ; def f(s: String) = ??? }

object Main extends App {
  X.f(Set(new B))
}

这里,value参数的类型为Set[B],然后它尝试并且无法找到对重载的param类型的隐式转换。

它还会查找将X对象转换为带有f方法的类型Set[B]

|    |-- X.f(Set(new B())) BYVALmode-EXPRmode (site: value <local Main> in Main)
|    |    |-- X.f BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local Main> in Main)
|    |    |    |-- X EXPRmode-POLYmode-QUALmode (silent: value <local Main> in Main)
|    |    |    |    \-> X.type
|    |    |    \-> (s: String)Nothing <and> (as: Set[A])Nothing
|    |    |-- Set(new B()) BYVALmode-EXPRmode (silent: value <local Main> in Main)
|    |    |    |-- Set BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local Main> in Main)
|    |    |    |    |-- scala.Predef.Set.apply BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local Main> in Main)
|    |    |    |    |    [adapt] [A](elems: A*)CC[A] adapted to [A](elems: A*)CC[A]
|    |    |    |    |    \-> (elems: A*)scala.collection.immutable.Set[A]
|    |    |    |    [adapt] => scala.collection.immutable.Set.type adapted to [A](elems: A*)CC[A]
|    |    |    |    \-> (elems: A*)scala.collection.immutable.Set[A]
|    |    |    |-- new B() BYVALmode-EXPRmode-POLYmode (silent: value <local Main> in Main)
|    |    |    |    |-- new B BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local Main> in Main)
|    |    |    |    |    |-- new B EXPRmode-POLYmode-QUALmode (silent: value <local Main> in Main)
|    |    |    |    |    |    |-- B FUNmode-TYPEmode (silent: value <local Main> in Main)
|    |    |    |    |    |    |    \-> B
|    |    |    |    |    |    \-> B
|    |    |    |    |    \-> ()B
|    |    |    |    \-> B
|    |    |    solving for (A: ?A)
|    |    |    \-> scala.collection.immutable.Set[B]
|    |    [search #1] start `(s: String)Nothing <and> (as: Set[A])Nothing`, searching for adaptation to pt=scala.collection.immutable.Set[B] => String (silent: value <local Main> in Main) implicits disabled
|    |    15 implicits in companion scope
|    |    [search #2] start `(s: String)Nothing <and> (as: Set[A])Nothing`, searching for adaptation to pt=(=> scala.collection.immutable.Set[B]) => String (silent: value <local Main> in Main) implicits disabled
|    |    15 implicits in companion scope
|    |    [search #3] start `(s: String)Nothing <and> (as: Set[A])Nothing`, searching for adaptation to pt=scala.collection.immutable.Set[B] => Set[A] (silent: value <local Main> in Main) implicits disabled
|    |    15 implicits in companion scope
|    |    [search #4] start `(s: String)Nothing <and> (as: Set[A])Nothing`, searching for adaptation to pt=(=> scala.collection.immutable.Set[B]) => Set[A] (silent: value <local Main> in Main) implicits disabled
|    |    15 implicits in companion scope
|    |    second try: <error> and Set(new B())
|    |    |-- Set(new B()) EXPRmode (silent: value <local Main> in Main)
|    |    |    |-- Set BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local Main> in Main)
|    |    |    |    |-- scala.Predef.Set.apply BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value <local Main> in Main)
|    |    |    |    |    [adapt] [A](elems: A*)CC[A] adapted to [A](elems: A*)CC[A]
|    |    |    |    |    \-> (elems: A*)scala.collection.immutable.Set[A]
|    |    |    |    [adapt] => scala.collection.immutable.Set.type adapted to [A](elems: A*)CC[A]
|    |    |    |    \-> (elems: A*)scala.collection.immutable.Set[A]
|    |    |    solving for (A: ?A)
|    |    |    \-> scala.collection.immutable.Set[B]
|    |    [search #5] start `X.type`, searching for adaptation to pt=X.type => ?{def f(x$1: ? >: scala.collection.immutable.Set[B]): ?} (silent: value <local Main> in Main) implicits disabled
|    |    [search #6] start `X.type`, searching for adaptation to pt=(=> X.type) => ?{def f(x$1: ? >: scala.collection.immutable.Set[B]): ?} (silent: value <local Main> in Main) implicits disabled
badset.scala:7: error: overloaded method value f with alternatives:
  (s: String)Nothing <and>
  (as: Set[A])Nothing
 cannot be applied to (scala.collection.immutable.Set[B])

答案 1 :(得分:2)

实际上......这个问题的真正答案隐藏在@ pamu的回答中。对此的回答有点不重要,需要大量解释。

让我们首先考虑一下编译的第一个案例,

class A {}
class B extends A {}

object Sample {
  def foo(a: Set[A]) {
    println("Hi Set[A]")
  }
}

Sample.foo(Set(new B()))

但为什么要编译?嗯......答案在于Scala编译器是一个非常聪明的生物,具有type-inference的能力。这意味着如果未明确提供类型,Scala会尝试通过查看可用信息来猜测用户可能想要的类型,并将其视为most suitable(最贴合)类型。

现在,在Sample.foo(Set(new B()))中,Scala发现fooSet[A]作为参数。它查看了提供的参数Set(new B()),它看起来更像Set[B] ...但是Scala编译器的主人&#34;程序员&#34;可以犯错因此,它会检查它是否可以将其推断为Set[A]。它成功了。 Scala编译器很高兴和自豪,它足够聪明,能够理解它的主要深刻意图。

Scala规范section 6.26.1将此称为Type Instantiation

更清楚地解释一下......让我展示当你明确告诉Scala类型时会发生什么,而Scala不需要使用任何推理智能。

// tell scala that it is a set of A
// and we all know that any set of A can contain B
scala> val setA: Set[A] = Set(new B())
setA: scala.collection.immutable.Set[A] = Set(B@17ae2a19)

// Scala is happy with a Set[A]
scala> Sample.foo(setA)
// Hi Set[A]

// tell scala that it is a set of B
// and we all know that any set of B can contain B
scala> val setB: Set[B] = Set(new B())
// setB: scala.collection.immutable.Set[B] = Set(B@17ae2a19)

// But Scala knows that Sample.foo needs a Set[A] and not Set[B]
scala> Sample.foo(setB)
// <console>:20: error: type mismatch;
//  found   : scala.collection.immutable.Set[B]
//  required: Set[A]
// Note: B <: A, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: A`. (SLS 3.2.10)
//        Sample.foo(setB)
              ^

现在我们知道为什么第一个案例适用于OP。让我们继续讨论第二种情况。

class A {}
class B extends A {}

object Sample {
  def foo(a: Set[A]) {
    println("Hi Set[A]")
  }
  def foo(a: String) {
    println("Hi A")
  }
}

Sample.foo(Set(new B()))

现在......突然Sample.foo(Set(new B()))无法编译。

原因再次隐藏在&#34;情报&#34; Scala编译器。 Scala编译器现在看到了两个Sample.foo。首先需要Set[A]而其他人需要String。 Scala应该如何决定程序员的意图。看看知道什么,Scala发现了更像Set[B]的东西。

现在,当我们讨论类型实例化和推理时,一旦scala知道期望什么类型,它就可以尝试推断该类型。但是Scala无法决定期望什么类型,因为它看到了多种选择。因此,在转向类型推断之前,它应该处理超载选择的问题,然后才能设置其对推理的期望。

这在Scala规范的Overload ResolutionSection 6.26.3)中进行了讨论。规范可能看起来有点不透明,所以我们来讨论它如何尝试区分。

overload resolution实际上由两个问题组成,

  • 问题1 ::只考虑所提供的论据,其中一个是更具体的applicable。换句话说,我们会查看关于可用替代方案的Applicability个论点。 Section 6.6中讨论了ApplicabilityApplicability首先考虑所提供参数的形状,并在很大程度上依赖于每个类型参数的CompatibilityConformance进行进一步分析。

  • 问题2 ::现在,考虑到方法调用的reference类型,我们尝试确定上面选择的替代方案中的哪一个Compatible

    < / LI>

现在,我们逐渐认识到Compatibility的重要性,Compatibility对此进行了详细讨论。简单来说,implicit views两种给定类型(不是函数)取决于implicit conversions(两种类型之间的Sample.foo(Set(new B()))

如果您遵守重载决策规则......您将看到Scala编译器无法解决调用Set[B]的多选问题。因此,不能进行任何推断,看起来最像Set[B]的论证仍被视为very in-accurate

将它放在type-inference中(只是为了更容易理解上面解释的实际问题,不应该以任何方式被视为准确),而是简单的解释 - &gt;你们都应该知道除了implicit conversions Scala之外还有另一个神奇的事情是在那些神奇的隐含type-class的帮助下调用Sample.foo。 Scala编译器现在看到了两个Set[A]。首先需要String而其他人需要Set[B]。但Scala看起来更像是infer。现在,它可以尝试Set[A] implicitly convert或尝试将String改为scala> Sample.foo(Set[A](new B())) // Hi Set[A] // Or for string scala> Sample.foo(Set[A](new B()).toString) // Hi A // Or, scala> val setA: Set[A] = Set(new B()) // setA: scala.collection.immutable.Set[A] = Set(B@17ae2a19) scala> Sample.foo(setA) // Hi Set[A] // Or for string scala> Sample.foo(setA.toString) // Hi A

对于Scala而言,这两种选择看起来都相当合理,现在这种选择对于智能&#34;对于它的高贵主人&#34;程序员&#34;通缉。它不敢在其主人的事务上犯任何错误,因此决定告诉大师其混乱并询问他的意愿。

现在......我们的程序员如何帮助解决它的困惑......我们只是提供更多信息。

例如,

{{1}}