gpt4 book ai didi

scala - 在宏注解主体中检测宏注解类型

转载 作者:行者123 更新时间:2023-12-03 23:41:50 27 4
gpt4 key购买 nike

我想使用宏注释(macro-paradise,Scala 2.11)在带注释的特征的伴随对象中生成合成特征。例如,给定一些 STM 抽象:

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]]
}

我已经产生了伴生对象、内在特征和 value成员。但显然,为了 next成员具有增强类型(而不是 Option[Cell[A]] ,我需要 Option[Cell.Txn[Tx, A]] ),我需要对类型树进行模式匹配并重写它。

例如,假设我找到 next原文 Cell中的值定义像这样的特质:
case v @ ValDef(vMods, vName, tpt, rhs) =>

如何分析 tpt递归地重写任何类型 X[...]@txn 注释至 X.Txn[Tx, ...] ?这甚至可能吗,就像上面的例子中给出的 X还没有处理?我应该修改 Cell混合要检测的标记特征?

所以模式匹配函数可以这样开始:
val tpt1 = tpt match {
case tq"$ident" => $ident // obviously don't change this?
case ??? => ???
}

最佳答案

我想以免责声明作为我的回答的序言,即在当前的 Scala 中这种事情并不容易做到。在理想的宏系统中,我们想对 tpt 进行类型检查。在其词法上下文中,然后遍历结果类型的结构,替换 X[A]X.Txn[Tx, A]对于那些 XTxnMarker 的子类型, 然后在宏展开中使用结果类型。

然而,这种无类型树(进入宏注释)和类型树(类型检查器发出)的愉快混合与编译器内部的工作方式不兼容(有关它的一些细节可以在 Scala macros: What is the difference between typed (aka typechecked) an untyped Trees 中找到),所以我们将必须近似。

这将是一个近似值,因为在 2.10 和 2.11 中,我们的宏都是不卫生的,这意味着它们容易受到名称冲突的影响(例如,在最终扩展树 Var 中的 Var[...] 可能绑定(bind)到不相关的东西,例如我们的特征重写包含一个名为 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 的循环依赖。 -带注释的定义。 info.baseClasses.contains(symbolOf[TxnMarker]) check 本质上会强制扩展 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]]
}

你实际上可以生成这个:
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使用 Type.map 进行转换,并且不会导致循环引用错误,因为在调用物化器时(在 typer 期间),所有宏注释都已经展开。不幸的是,我现在没有时间详细说明,但这看起来可行!

关于scala - 在宏注解主体中检测宏注解类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22275720/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com