如何在Scala中使用宏创建变量

时间:2013-09-17 09:59:14

标签: scala macros

我的问题可能是人为的,我想将其视为可行性证明,而不是推荐的东西。 为了给你一些上下文,我会尝试在嵌入式DSL中使用与

不同的语法
val myVal = "someContent"

我希望能够做到这样的事情:

assign("SomeContent","someVariable") => converted to
someVariable = "SomeContent"

然后在代码中,变量可用。 通常我们可以在REPL中使用类似的东西:

assign("John","name")
println("Hello " + name)

我一直在考虑使用宏(或ScalaCompiler插件,但我认为这里更复杂)才能做到这一点。 首先,我不知道这对宏来说是否可行。

考虑到我只会操作String而我开始使用类似

的东西,我已经开始了一些简单化的东西
def assign(content: String, targetVal: String):Unit = macro assignMacro

def assignMacro(c:Context)(content: c.Expr[String], 
                           targetVal: c.Expr[String]):c.expr[Unit] = {
  import c.universe._

  c.Expr[Unit](ValDef(Modifiers(), TermName(targetVal.value),
                      TypeTree(), Literal(Constant(content.value)))
}

不幸的是,它似乎因为几个错误而失败

  • 首先它抱怨当我尝试创建一个新的术语名称,表明如果我确定那么我应该调用我的表达式的eval。不幸的是我不确定;)如果我尝试,它会失败;)
  • 如果我用myVal和myContent等常量替换此targetVal和内容,我会收到第二条错误消息,例如找到编译器并且需要Unit

我有点卡住了。 首先这可能吗?我猜是的;) 我怎么能实现这个目标呢?

感谢您的帮助

最好的问候

1 个答案:

答案 0 :(得分:3)

2.10和2.11中的def宏都扩展为块,这意味着它们引入的定义变为该块的本地定义。这可能会在宏观天堂发生变化,但我不确定何时以及如何。

目前,Scala中的宏没有提供将新变量透明地引入现有范围的方法。这是Scala宏的演化历史工件,这是由于我们的反射组优先考虑黑盒宏(宏的行为类似于常规方法,完全由其类型签名描述,因此人类和程序可以将其实现视为黑盒而不会丢失)任何事情)。

最近对whitebox宏的兴趣(不适合黑盒方案的宏)产生了一些结果,但这些结果尚未包含在主线Scala中。我将在明天的Strange Loop演讲中详细阐述这个问题:https://thestrangeloop.com/sessions/evolution-of-scala-macros


与此同时,您可以查看可能提供您正在寻找的自由度的宏注释。例如,通过适当地定义mydsl注释,您可以拥有

@mydsl 
def foo = {
  assign("SomeContent", "someVariable")
  ...
}

转变为

def foo = {
  someVariable = "SomeContent"
  ...
}