简体   繁体   English

Scala闭包上下文

[英]Scala closure context

I am not a Groovy expert, but I did read the book "Groovy in Action". 不是 Groovy专家,但我读过“Groovy in Action”这本书。 In Groovy, each closure comes with a "context", where the items inside the closure can get access to pseudo-variables like "this", "owner", and "delegate", that let the items know who called the closure. 在Groovy中,每个闭包都带有一个“上下文”,其中闭包内的项可以访问伪变量,如“this”,“owner”和“delegate”,让项目知道谁调用了闭包。 This allows one to write DSLs like this (from Groovy in Action): 这允许人们编写这样的DSL(来自Groovy in Action):

swing = new SwingBuilder()
frame = swing.frame(title:'Demo') {
  menuBar {
    menu('File') {
      menuItem 'New'
      menuItem 'Open'
    }
  }
  panel {
    // ...
  }
}

Note that 'menuBar' "knows" that it belongs to 'frame' because it can get context information about the owner and delegate of the closure. 请注意,'menuBar'“知道”它属于'frame',因为它可以获取有关闭包的所有者和委托的上下文信息。

Is this possible to do in Scala? Scala可以做到这一点吗? If so, how? 如果是这样,怎么样?

One way is to use a scala.util.DynamicVariable to track the context. 一种方法是使用scala.util.DynamicVariable来跟踪上下文。 Something like the SwingBuilder could be implemented as 像SwingBuilder这样的东西可以实现为

import scala.util.DynamicVariable
import javax.swing._

object SwingBuilder {
  case class Context(frame: Option[JFrame], parent: Option[JComponent])
}

class SwingBuilder {
  import SwingBuilder._
  val context = new DynamicVariable[Context](Context(None,None))

  def frame(title: String)(f: =>Unit) = {
    val res = new JFrame(title)
    res.add(new JPanel())
    context.withValue(Context(Some(res),context.value.parent)){f;res}
  }

  def menuBar(f: =>Unit) = {
    val mb = new JMenuBar()
    context.value.frame.foreach(_.setJMenuBar(mb))
    context.withValue(Context(context.value.frame,Some(mb))){f;mb}
  }

  def menu(title: String)(f: =>Unit) = {
    val m = new JMenu(title)
    context.value.parent.foreach(_.asInstanceOf[JMenuBar].add(m))
    context.withValue(Context(context.value.frame,Some(m))){f;m}
  }

  def menuItem(title: String) = {
    val mi = new JMenuItem(title)
    context.value.parent.foreach(_.asInstanceOf[JMenu].add(mi))
  }
}

object Test {
  def main(args: Array[String]) {
    val builder = new SwingBuilder()
    import builder._

    val f = frame("Demo") {
      val mb = menuBar {
        menu("File") {
          menuItem("New")
          menuItem("Open")
        }
      }
    }
    f.setVisible(true)
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM