Я не эксперт Groovy, но я действительно читал книгу "Groovy в Действии". В Groovy каждое закрытие идет с "контекстом", где объекты в закрытии могут получить доступ к псевдопеременным как "это", "владелец" и "делегат", которые сообщают объектам, кто назвал закрытие. Это позволяет писать DSLs как это (от Groovy в Действии):
swing = new SwingBuilder()
frame = swing.frame(title:'Demo') {
menuBar {
menu('File') {
menuItem 'New'
menuItem 'Open'
}
}
panel {
// ...
}
}
Обратите внимание, что 'строка меню' "знает", что принадлежит 'кадру', потому что это может получить контекстную информацию о владельце и делегате закрытия.
Действительно ли это возможно сделать в Scala? Если так, как?
Один из способов - использовать 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)
}
}