在scala宏(2.10)中使用LabelDef
我正在试验scala 2.10的宏功能。不过,在某些情况下,我很难使用LabelDef。在某种程度上,我偷看了编译器的代码,阅读了的摘录,但我仍然被卡住了。在scala宏(2.10)中使用LabelDef,scala,macros,scala-2.10,scala-macros,Scala,Macros,Scala 2.10,Scala Macros,我正在试验scala 2.10的宏功能。不过,在某些情况下,我很难使用LabelDef。在某种程度上,我偷看了编译器的代码,阅读了的摘录,但我仍然被卡住了。 如果我的理解是正确的,那么伪定义应该是: LabelDef(labelName、参数列表、stm和apply)其中3个参数是树和: -labelName是正在定义的标签$L的标识符 -listOfParameters对应于发生label apply时传递的参数,如$L(a1,…,an),可以为空 -stmsAndApply对应于语句块(可能
如果我的理解是正确的,那么伪定义应该是:
LabelDef(labelName、参数列表、stm和apply)
其中3个参数是树和:-
labelName
是正在定义的标签$L的标识符-
listOfParameters
对应于发生label apply时传递的参数,如$L(a1,…,an),可以为空-
stmsAndApply
对应于语句块(可能无)和最终应用表达式label apply(标签应用)或多或少意味着转到标签
例如,在一个简单循环的情况下,LabelDef最终可以应用自身:
LabelDef($L,(),{…;$L()})
现在,如果我想定义两个相互跳转的LabelDef:
...
LabelDef($L1, (), $L2())
...
LabelDef($L2, (), $L1())
...
第二个LabelDef很好,但编译器在第1个LabelDef上输出一个错误,“未找到:值$L2”。我想这是因为$L2尚未定义,但有人试图应用它。这是一棵对我有意义的树。到目前为止,我的理解正确吗?因为如果没有预期的错误,这意味着我的宏实现可能有缺陷
无论如何,我相信一定有一种方法可以从$L1应用$L2(即跳到$L2),但我就是不知道该怎么做。有人有这样做的例子吗,或者有什么指针吗
关于在宏中使用LabelDef的其他不清楚的地方(但现在不太关心)有:
-第二个参数是什么,具体来说,非空时如何使用?换句话说,标签应用参数的机制是什么?
-在第三个参数的最后一个表达式中放入标签以外的任何内容是否有效?(并不是说我不能尝试,但宏仍然是实验性的)
-是否可以在标签外执行转发标签应用?(也许这是一个多余的问题) 当然,答案中的任何宏实现示例都是非常受欢迎的
干杯,因为如果没有预期的错误,这意味着我的宏实现可能有问题。
是的,这似乎是一个bug(^^;尽管我不确定Block/LabelDef组合的限制是否故意存在
def EVIL_LABELS_MACRO = macro EVIL_LABELS_MACRO_impl
def EVIL_LABELS_MACRO_impl(c:Context):c.Expr[Unit] = { // fails to expand
import c.universe._
val lt1 = newTermName("$L1"); val lt2 = newTermName("$L2")
val ld1 = LabelDef(lt1, Nil, Block(c.reify{println("$L1")}.tree, Apply(Ident(lt2), Nil)))
val ld2 = LabelDef(lt2, Nil, Block(c.reify{println("$L2")}.tree, Apply(Ident(lt1), Nil)))
c.Expr( Block(ld1, c.reify{println("ignored")}.tree, ld2) )
}
def FINE_LABELS_MACRO = macro FINE_LABELS_MACRO_impl
def FINE_LABELS_MACRO_impl(c:Context):c.Expr[Unit] = { // The End isn't near
import c.universe._
val lt1 = newTermName("$L1"); val lt2 = newTermName("$L2")
val ld1 = LabelDef(lt1, Nil, Block(c.reify{println("$L1")}.tree, Apply(Ident(lt2), Nil)))
val ld2 = LabelDef(lt2, Nil, Block(c.reify{println("$L2")}.tree, Apply(Ident(lt1), Nil)))
c.Expr( Block(ld1, c.reify{println("ignored")}.tree, ld2, c.reify{println("The End")}.tree) )
}
我认为一个块被解析为{statements;expression},因此最后一个参数是表达式。如果一个LabelDef“落在”表达式中,例如,EVIL_LABELS_宏模式,它的扩展将在语句中不可见;因此出现错误“not found:value$L2”
因此,最好确保所有LabelDef“fall in”语句。FINE_LABELS_MACRO这样做并扩展到:
{
$L1(){
scala.this.Predef.println("$L1");
$L2()
};
scala.this.Predef.println("ignored");
$L2(){
scala.this.Predef.println("$L2");
$L1()
};
scala.this.Predef.println("The End")
}
在@Eugene问这个问题可能是个好主意,我最终不得不阅读相关的scala内部线程。然而,似乎已经决定(并实现)从scala 2.11中删除
LabelDef
.虽然我从内部角度理解其动机,但我相信,基于LabelDef
的具有GOTO
-ish功能的DSL将受到影响(例如,不是JVMGOTO
指令,而是一组变通指令)。你对此有什么意见吗?它肯定还没有实现,因为LabelDefs仍然存在于2.11分支中:。我在-上问了一个问题,请加入并分享你的担忧。@Eugene干杯。我想一个贡献者已经在他的私有克隆库中实现了更改,我一定是误读了。我的Scala技能集不包括内部,因此我在讨论组中犹豫了一下。但我会接受你的建议,我会这样做。到目前为止,我只是从编译器最终用户(即程序员)的角度提出了要求,因为宏API是公开的(尽管是实验性的)。它能以空树
结尾吗?我认为空树
没有附加类型,甚至没有scala。没有任何内容
,因此不适用于表达式。在我的示例中,永远不会达到结尾,但是c.Expr
的块
可以以文本(常量(())结尾)
相反,由于宏必须产生一个单元
@daniel:如果你的意思是LabelDef
,我认为第三个参数的树也应该产生一个表达式,因此没有EmptyTree
。我不知道这在将来是否仍然有效,但第三个参数不一定是一个标签。