使用块选择隐式转换位置



在下面的代码中,在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()
}

我将这种行为合理化如下:根据规范,块{s1; s2; ...; sn; e }的类型是最后一个表达式e的类型。

编译器将eRunnable进行类型检查。这失败了,所以它搜索将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())
});

根据规范,当表达式的类型与期望的类型不匹配时,应用隐式转换。关键的观察是,当输入块时,期望的类型是如何线程化的。

如果表达式e的类型为T,且T不符合表达式期望的类型 pt,则

。在本例中,搜索适用于e且其结果类型符合pt的隐式v。

6.11 Blocks小节中,块最后表达式的预期类型定义为

最终表达式e的期望类型是块的期望类型。

根据这个规范,编译器似乎有这样的行为。块的期望类型是Runnable, println(2)的期望类型也变成了Runnable

一个建议:如果你想知道应用了什么隐式,你可以使用Eclipse的Scala IDE 2.1的夜间构建。它可以'highlight implicit '。

编辑:我承认在作用域中隐式调用名字是令人惊讶的。

问题是你把块当成了一堆东西,当成了一段代码。他们不是。{ a; b; c }而不是一段可以传递的代码。

那么,你应该如何推断隐含式呢?实际上,您应该如何推断视图,它们是隐式转换。视图应用于需要更改的值。在您的示例中,

的值
{
    println(1)
    println(2)
}

正在传递给setRunnable。块的值是它的最后一个表达式的值,因此它将println(2)的结果传递给setRunnable。因为是Unit,而setRunnable需要一个Runnable,所以搜索并找到了一个隐式,所以println(2)被传递给了严重错误命名的blockToRunnable

最重要的是,这是我已经在Stack Overflow上给过很多次的建议(很多人都试图做同样的事情),在你的头脑中得到以下内容:

THERE ARE NO BLOCKS IN SCALA.

有函数,但没有块。

从技术上讲,这种说法是不正确的——Scala中有块,但它们不是你想象的那样,所以把它们从你的脑海中完全移除。稍后,您可以从头开始学习Scala中的块是什么。否则,你一定会试图让它们以它们不应该的方式工作,或者在它们以不同的方式工作时推断出它们以某种方式工作。

我非常喜欢第一个scala谜题中的解释。

也就是说,

的输出是什么?
List(1, 2).map { i => println("Hi"); i + 1 }
List(1, 2).map { println("Hi"); _ + 1 }

最新更新