Контекст закрытия Scala

Я не эксперт Groovy, но я действительно читал книгу "Groovy в Действии". В Groovy каждое закрытие идет с "контекстом", где объекты в закрытии могут получить доступ к псевдопеременным как "это", "владелец" и "делегат", которые сообщают объектам, кто назвал закрытие. Это позволяет писать DSLs как это (от Groovy в Действии):

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

Обратите внимание, что 'строка меню' "знает", что принадлежит 'кадру', потому что это может получить контекстную информацию о владельце и делегате закрытия.

Действительно ли это возможно сделать в Scala? Если так, как?

7
задан Brian Tompsett - 汤莱恩 5 June 2015 в 22:05
поделиться

1 ответ

Один из способов - использовать scala. util.DynamicVariable для отслеживания контекста. Что-то вроде 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)
  }
}
16
ответ дан 6 December 2019 в 12:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: