trait Var[Tx,A] { def apply() (implicit tx: Tx): A def update(value: A)(implicit tx: Tx): Unit }
我想定义一个宏注释txn,以便:
@txn trait Cell[A] { val value: A var next: Option[Cell[A]] }
将重新合成为:
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]] }
我得到了伴侣对象,内部特征和价值成员.但显然,为了让下一个成员拥有增强类型(而不是Option [Cell [A]],我需要Option [Cell.Txn [Tx,A]]),我需要模式匹配类型树并重写它.
例如,假设我在原始Cell特征中找到下一个值定义,如下所示:
case v @ ValDef(vMods,vName,tpt,rhs) =>
如何递归地分析tpt以将用@txn注释的任何类型X […]重写为X.Txn [Tx,…]?这是否可能,如上例所示,X尚未处理?我应该修改细胞以混合待检测的标记性状吗?
所以模式匹配函数可以这样开始:
val tpt1 = tpt match { case tq"$ident" => $ident // obvIoUsly don't change this? case ??? => ??? }
解决方法
然而,这种非类型树(进入宏注释)和类型树(类型检查器发出)的快乐混合与编译器内部工作方式不相容(有关详细信息可以在Scala macros: What is the difference between typed (aka typechecked) an untyped Trees中找到),所以我们必须近似.
这将是一个近似值,因为在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 }
实际上,当我编写这个宏时,我意识到处理@txn-annotated定义的循环依赖性是不够的. info.baseClasses.contains(symbolOf [TxnMarker])检查实质上强制扩展info中引用的类/特征,因此宏将循环.这不会导致SOE或冻结 – scalac只会产生循环参考错误并挽救.
目前我不知道如何使用宏完全解决这个问题.也许您可以将代码生成部分保留在注释宏中,然后将类型转换部分移动到fundep实现器中.哦,对,似乎它可能会奏效!而不是生成
object Cell { trait Txn[-Tx,A]]] } } trait Cell[A] { val value: A var next: Option[Cell[A]] }
你实际上可以生成这个:
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]] }
并且TxnTypeMapper [T,U]可以通过fundep materializer macro召唤来进行Type =>使用Type.map进行类型转换,并且不会导致循环引用错误,因为在调用实现器时(在typer期间),所有宏注释都已经扩展.不幸的是,我现在没有时间详细说明,但这看起来很可行!