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)
被注释掉时成功编译的原因。
答案 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发现foo
将Set[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 Resolution
(Section 6.26.3)中进行了讨论。规范可能看起来有点不透明,所以我们来讨论它如何尝试区分。
overload resolution
实际上由两个问题组成,
问题1 ::只考虑所提供的论据,其中一个是更具体的applicable
。换句话说,我们会查看关于可用替代方案的Applicability
个论点。 Section 6.6中讨论了Applicability
。 Applicability
首先考虑所提供参数的形状,并在很大程度上依赖于每个类型参数的Compatibility
和Conformance
进行进一步分析。
问题2 ::现在,考虑到方法调用的reference
类型,我们尝试确定上面选择的替代方案中的哪一个Compatible
。
现在,我们逐渐认识到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}}