使用块选择隐式转换位置

时间:2011-12-20 15:08:36

标签: scala implicit

在以下代码中,隐式转换应用于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()

}

4 个答案:

答案 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。由于UnitsetRunnable需要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 }