斯卡拉 DSL : invocation that mimics English

标签 scala dsl

我是 scala 的新手,这更像是一个好奇的问题。

假设我有一个类

class Container()
{
    def add(item: Item) ...
}

我可以这样调用它:container add item。 我想知道如何将此调用变成类似英语的 add item to container?它可能会违反风格指南,但正如我所说,我只是好奇。

最佳答案

因为我喜欢好的挑战,所以我尝试了如果您滥用 Scala 必须提供的几乎所有危险和/或实验性功能,您是否无法到达您想要的地方,结果证明这是可能的:

scala> :pa
// Entering paste mode (ctrl-D to finish)

import scala.language.dynamics
import scala.language.postfixOps
import scala.language.experimental.macros

import scala.reflect.macros.blackbox.Context

case class Item(i: Int)
class Container { var item: Item = null; def add(i: Item) = item = i }

object to
object add extends Dynamic { def applyDynamic(item: String)(a: to.type) = new AddContainer}
class AddContainer extends Dynamic { def selectDynamic(container: String): Unit = macro addMacro }

/**
 * Don't try this at home: not a foolproof macro!
 */
def addMacro(c: Context)(container: c.Tree): c.Tree = {
  import c.universe._
  val Literal(Constant(containerName: String)) = container
  val q"$_.applyDynamic($item)($_)" = c.prefix.tree
  val Literal(Constant(itemName: String)) = item
  q"${TermName(containerName)}.add(${TermName(itemName)})"
}

// Exiting paste mode, now interpreting.


scala> object Test {
     |   val item = Item(1)
     |   val container = new Container
     |   println(container.item)
     | 
     |   add item to container
     | 
     |   println(container.item)
     | }
defined object Test

scala> Test
null
Item(1)

让我们把它分解成可以理解的部分。

您想编写 add item to container,它被解析为 add.item(to).container。实现的后果是:

  • 您需要启用 postfixOps,以便可以将 container 用作后缀运算符。
  • itemcontainer 都在方法调用位置,但我们希望它们引用当前范围内的变量。为了克服这个问题,我们需要结合两个特性:
    • Dynamic我们可以将 a.b(c) 转换为 a.applyDynamic("b")(c) 并将 a.b 转换为 a.selectDynamic( “b”)。因此,方法名称变成了我们可以动态处理的字符串。
    • 然后我们需要一个宏来将这些字符串转换为引用我们变量的标识符。我们从语法树中提取 String 文字,将它们转换为 TermName 并输出一个新的语法树,看起来像 container.add(item).

为简单起见,此宏仅适用于最微不足道的情况。要使它可靠地工作可能需要付出更多的努力。并且非常清楚:这只是为了回答“需要什么”的问题。我怀疑是否有一个用例可以证明实际这样做是合理的。然而,此处使用的技术可能适用于一些非常高级但有效的用例。

关于斯卡拉 DSL : invocation that mimics English,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51965278/

相关文章:

python - Python中的迷你语言

scala - 如何在查询参数中喷射解码列表

scala - UUID 路径可绑定(bind) - Play Framework

scala - 为什么 Scala 2.11.2 会给我科学记数法 float 的编译错误?

scheme - 有没有关于为 PLT Scheme 编写自定义语言模块的好教程?

c - "resettable"带有 C 宏的循环 DSL?

clojure - 是否有Clojure DSL?

scala - 无法识别的 VM 选项 'CMSClassUnloadingEnabledn-J-Xmx2Gn'

scala - Scala 中的 list 是什么?什么时候需要它?

spring - 如何加入多个queryDSL表