我想使用宏注释(macro-paradise,Scala 2.11)在带注释的特征的伴随对象中生成合成特征.例如,给定一些STM抽象:
trait Var[Tx, A] {
def apply() (implicit tx: Tx): A
def update(value: A)(implicit tx: Tx): Unit
}
Run Code Online (Sandbox Code Playgroud)
我想定义一个宏注释txn,以便:
@txn trait Cell[A] {
val value: A
var next: Option[Cell[A]]
}
Run Code Online (Sandbox Code Playgroud)
将重新合成为:
object Cell {
trait Txn[-Tx, A] {
def value: A
def next: Var[Option[Cell.Txn[Tx, A]]] // !
}
}
trait Cell[A] {
val value: A
var next: Option[Cell[A]]
}
Run Code Online (Sandbox Code Playgroud)
我得到了伴侣对象,内在特征和value成员.但很明显,为了让next成员拥有扩充类型(而不是Option[Cell[A]]我需要Option[Cell.Txn[Tx, A]]),我需要模式匹配类型树并重写它.
例如,假设我next在原始Cell特征中找到值定义,如下所示:
case v @ ValDef(vMods, vName, tpt, rhs) =>
Run Code Online (Sandbox Code Playgroud)
如何分析tpt递归改写任何类型的X[...]被标注了@txn到X.Txn[Tx, ...]?这是否可能,就像上面的例子中那样X,还有待处理?我应该修改Cell混合标记特征进行检测吗?
所以模式匹配函数可以这样开始:
val tpt1 = tpt match {
case tq"$ident" => $ident // obviously don't change this?
case ??? => ???
}
Run Code Online (Sandbox Code Playgroud)
我想在免责声明中说明我的回答,这种东西在当前的Scala中并不容易.在一个理想的宏系统,我们想类型检查tpt在其词法上下文中,然后通过将得到的类型替换的结构行走X[A]与X.Txn[Tx, A]那些X属于亚型TxnMarker,然后用所得的类型中的宏扩展.
然而,这种非常类型的树(进入宏注释)和类型树(typechecker发出的)的快乐混合与编译器内部工作的方式不兼容(关于它的一些细节可以在Scala宏中找到:typed之间的区别是什么(又名typechecked)一个无类型的树),所以我们必须近似.
这将是一个近似值,因为在2.10和2.11中我们的宏都是不卫生的,这意味着它们容易受到名称冲突的影响(例如,在最终的扩展树Var中Var[...],如果我们正在重写的特性包含一个类型成员,则可以绑定到不相关的东西)叫Var).不幸的是,目前只有一种方法可以有效地解决这个问题,而且如果不深入理解编译器内部结构就非常难以实现,所以我不会在这里讨论这些细节.
import scala.reflect.macros.whitebox._
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
trait Var[T]
trait TxnMarker
object txnMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
// NOTE: this pattern is only going to work with simple traits
// for a full pattern that captures all traits, refer to Denys's quasiquote guide:
// http://den.sh/quasiquotes.html#defns-summary
val q"$mods trait $name[..$targs] extends ..$parents { ..$stats }" = annottees.head.tree
def rewire(tpt: Tree): Tree = {
object RewireTransformer extends Transformer {
override def transform(tree: Tree): Tree = tree match {
case AppliedTypeTree(x @ RefTree(xqual, xname), a :: Nil) =>
val dummyType = q"type SomeUniqueName[T] = $x[T]"
val dummyTrait = q"$mods trait $name[..$targs] extends ..$parents { ..${stats :+ dummyType} }"
val dummyTrait1 = c.typecheck(dummyTrait)
val q"$_ trait $_[..$_] extends ..$_ { ..${_ :+ dummyType1} }" = dummyTrait1
def refersToSelf = dummyTrait1.symbol == dummyType1.symbol.info.typeSymbol
def refersToSubtypeOfTxnMarker = dummyType1.symbol.info.baseClasses.contains(symbolOf[TxnMarker])
if (refersToSelf || refersToSubtypeOfTxnMarker) transform(tq"${RefTree(xqual, xname.toTermName)}.Txn[Tx, $a]")
else super.transform(tree)
case _ =>
super.transform(tree)
}
}
RewireTransformer.transform(tpt)
}
val stats1 = stats map {
// this is a simplification, probably you'll also want to do recursive rewiring and whatnot
// but I'm omitting that here to focus on the question at hand
case q"$mods val $name: $tpt = $_" => q"$mods def $name: $tpt"
case q"$mods var $name: $tpt = $_" => q"$mods def $name: Var[${rewire(tpt)}]"
case stat => stat
}
val annottee1 = q"$mods trait $name[..$targs] extends ..${parents :+ tq"TxnMarker"} { ..$stats }"
val companion = q"""
object ${name.toTermName} {
trait Txn[Tx, A] { ..$stats1 }
}
"""
c.Expr[Any](Block(List(annottee1, companion), Literal(Constant(()))))
}
}
class txn extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro txnMacro.impl
}
Run Code Online (Sandbox Code Playgroud)
实际上,当我编写这个宏时,我意识到它没有能力处理@txn注释定义的循环依赖.该info.baseClasses.contains(symbolOf[TxnMarker])检查实质上会强制类/性状的膨胀中提到的info,所以宏将要循环.这不会导致SOE或冻结 - scalac只会产生循环参考错误并挽救.
目前我不知道如何使用宏完全解决这个问题.也许您可以将代码生成部分保留在注释宏中,然后将类型转换部分移动到fundep实现器中.哦,对,似乎它可能会奏效!而不是生成
object Cell {
trait Txn[-Tx, A] {
def value: A
def next: Var[Option[Cell.Txn[Tx, A]]]
}
}
trait Cell[A] {
val value: A
var next: Option[Cell[A]]
}
Run Code Online (Sandbox Code Playgroud)
你实际上可以生成这个:
object Cell {
trait Txn[-Tx, A] {
def value: A
def next[U](implicit ev: TxnTypeMapper[Option[Cell[A]], U]): U
}
}
trait Cell[A] {
val value: A
var next: Option[Cell[A]]
}
Run Code Online (Sandbox Code Playgroud)
并且TxnTypeMapper[T, U]可以通过一个fundep物化器宏来召唤,该宏将Type => Type使用转换Type.map,并且不会导致循环引用错误,因为在调用物理化器时(在typer期间),所有宏注释都已经扩展.不幸的是,我现在没有时间详细说明,但这看起来很可行!