在以下代码中,隐式转换应用于println(2)
行;我愚蠢地期望它适用于整个区块{ println(1); println(2) }
。我应该如何推断编译器放置隐式的位置?
object Executor {
private var runnable: Runnable = _
def setRunnable(runnable: Runnable) {
this.runnable = runnable
}
def execute() { runnable.run() }
}
object Run extends App {
implicit def blockToRunnable(p: ⇒ Any): Runnable =
new Runnable { def run() = p }
Executor.setRunnable {
println(1)
println(2)
}
println("Before execute")
Executor.execute()
}
答案 0 :(得分:4)
我合理化这种行为:根据规范,块{s1; s2; ...; sn; e }
的类型是最后一个表达式e
的类型。
因此编译器需要e
并对Runnable
进行类型检查。该操作失败,因此会搜索将e
转换为Runnable
的隐式转换。所以它想这样:
{ s1; s2; ... sn; convert(e) }
这个小例子的scala -Xprint:typer
证实了这一点:
class A
implicit def convert(a: A): String = a.toString
def f(s: String) { println(s) }
f{ println(1); new A }
打印:
private[this] val res0: Unit = $line3.$read.$iw.$iw.f({
scala.this.Predef.println(1);
$line2.$read.$iw.$iw.convert(new $line1.$read.$iw.$iw.A())
});
答案 1 :(得分:3)
根据规范,当表达式的类型与期望的类型不匹配时,将应用隐式转换。关键的观察是在键入块时如何线程化预期的类型。
如果表达式
e
的类型为T
,且T不符合表达式的期望类型pt
。在这种情况下,搜索隐式v,其适用于e并且其结果类型符合pt。
在 6.11 Blocks 部分中,块的最后一个表达式的预期类型定义为
最终表达式
e
的预期类型是块的预期类型。
鉴于此规范,似乎编译器具有以这种方式运行。预期的块类型为Runnable
,预期的println(2)
类型也会变为Runnable
。
建议:如果您想知道应用了哪些implicits,可以使用适用于Eclipse的Scala IDE 2.1的每晚构建。它可以“突出暗示”。
已编辑:我承认,当范围内隐含着一个名字时,我会感到惊讶。
答案 2 :(得分:0)
问题是你正在考虑阻塞就好像它们是thunk一样,就像它们是代码片段一样。他们不是。 { a; b; c }
不是可以传递的代码片段。
那么,你应该怎么推理暗示呢?实际上,你应该如何推理 views ,它们是隐式转换。视图应用于需要更改的值。在您的示例中,
的值{
println(1)
println(2)
}
正在传递给setRunnable
。块的值是其最后一个表达式的值,因此它将println(2)
的结果传递给setRunnable
。由于Unit
而setRunnable
需要Runnable
,因此会搜索并找到隐式,因此println(2)
会传递给名称错误的blockToRunnable
。< / p>
底线是,这是我在Stack Overflow上已经多次提出的一个建议(很多人试图做同样的事情)是让你的头脑中有以下几点:
THERE ARE NO BLOCKS IN SCALA.
有功能,但没有阻止。
从技术上讲,该语句不正确 - Scala中存在块,但它们并不是您认为的那样,所以只需将它们从脑海中彻底删除即可。您可以从干净的平板中了解Scala中的哪些块。否则,你必然会尝试让他们以他们不这样做的方式工作,或者推断当事情以不同的方式工作时,事情会以某种方式发挥作用。
答案 3 :(得分:0)
我很喜欢first scala puzzle中给出的解释。
换句话说,输出的是什么:
List(1, 2).map { i => println("Hi"); i + 1 }
List(1, 2).map { println("Hi"); _ + 1 }