16

Scala allows closure like

def newCounter = {
  var a=0
  () => {a+=1;a}
}

which defines a function that on every call returns a new independent counter function starting at 1:

scala> val counter1 = newCounter
counter1: () => Int = <function0>

scala> counter1()
res0: Int = 1

scala> counter1()
res1: Int = 2

scala> val counter2 = newCounter
counter2: () => Int = <function0>

scala> counter2()
res2: Int = 1

scala> counter1()
res3: Int = 3

This is quite impressive as usually a would be a representative of a memory address on the stack frame of newCounter. I've just read the closure chapter of "Programming in Scala" and it only has the following to say on that matter (p. 155):

The Scala compiler rearranges things in cases like this so that the captured parameter lives out on the heap, instead of the stack, and thus can outlive the method call that created it. This rearrangement is all taken care of automatically, so you don't have to worry about it.

Can anyone elaborate on how this works on byte code level? Is the access similar to a member variable of a class with all the associated synchronization and performance implications?

4

1 回答 1

16

你可以scalac -Xprint:lambdalift <scala-file-name>用来调查这个。

您的代码实际上是这样的:

def newCounter = {
  val a: runtime.IntRef = new runtime.IntRef(0);
  new Function0 {
    private[this] val a$1 = a
    def apply() = {
      a$1.elem = a$1.elem + 1
      a$1.elem
    }
  }
}

varlambda 使用的任何内容都有一个包装器。其他vars(不用于闭包)是常见的语言环境变量。

此包装器的链接存储为函数实例中的字段。

lambdaliftin-Xprint:lambdalift编译阶段。您可以使用-Xshow-phases. 您可以使用阶段编号而不是名称,这在您不确定需要哪个阶段时很有用。

于 2013-06-16T14:32:35.343 回答