Scala宏创建字段和方法指针
我想创建一个Scala宏&对于字段,它将返回一个getter/setter对,对于方法,它将返回一个部分应用的函数。如下所示:Scala宏创建字段和方法指针,scala,scala-macros,Scala,Scala Macros,我想创建一个Scala宏&对于字段,它将返回一个getter/setter对,对于方法,它将返回一个部分应用的函数。如下所示: trait ValRef[T] { def get(): T } trait VarRef[T] extends ValRef[T] { def set(x: T): Unit } // Example: case class Foo() { val v = "whatever" var u = 100 def m(x: Int
trait ValRef[T] {
def get(): T
}
trait VarRef[T] extends ValRef[T] {
def set(x: T): Unit
}
// Example:
case class Foo() {
val v = "whatever"
var u = 100
def m(x: Int): Unit
}
val x = new Foo
val cp: ValRef[String] = &x.v
val p: VarRef[Int] = &x.u
p.set(300)
val f: Int => Unit = &x.m
f(p.get())
我没有使用Scala宏的经验,但是我认为这对于那些使用Scala宏的人来说应该是相当简单的 我最近开始阅读Scala的宏,发现你的问题是一个有趣的练习。我只实现了指向
val
和var
值的指针,但由于您只需要代码的草图,我想我会分享我到目前为止的发现
编辑:关于指向方法的指针:如果我没有误解的话,这已经是Scala的一个特性了。给定类Foo{defm(i:Int):Unit=println(i)}
,您将得到一个函数valf:Int=>Unit=newfoo().m
- 如果您只对代码感兴趣,请滚动至答案的底部李>
trait ValRef[T] {
def get(): T
}
trait VarRef[T] extends ValRef[T] {
def set(x: T): Unit
}
现在我们想要实现一个方法&
,对于给定的引用,即name
或qualifier.name
,它返回一个ValRef
。如果引用引用了一个可变值,那么结果应该是一个VarRef
def &[T](value: T): ValRef[T]
到目前为止,这只是普通的Scala代码。方法&
接受任何表达式并返回与参数类型相同的ValRef
让我们定义一个宏来实现和的逻辑:
def pointer[T: c.WeakTypeTag](c: scala.reflect.macros.blackbox.Context)(value: c.Expr[T]) = ???
签名应主要是标准的:
c
-上下文包含使用宏的编译器收集的信息李>
T
是传递给&
value
对应于&
的参数,但由于实现是在Scala的AST上运行的,因此它是Expr[T]
类型,而不是原始类型T
有一点特别的是使用了WeakTypeTag
,我自己并不完全理解。各国:
实现中的类型参数可能带有WeakTypeTag上下文边界。在这种情况下,当宏展开时,将传递描述在应用程序站点实例化的实际类型参数的相应类型标记
有趣的部分是指针的实现。由于只要调用方法&
,编译器就应该使用该方法的结果,因此它必须返回一个。因此,我们想要生成一棵树。问题是,这棵树应该是什么样子
幸运的是,自Scala2.11以来,存在一种称为。Quasiquotes可以帮助我们从字符串值构建树
让我们首先简化问题:我们总是返回一个VarRef
,而不是区分val
和var
引用。对于由x.y
get()
应返回x.y
set(x)
应执行x.y=x
因此,我们想要生成一个树,它表示VarRef[T]
的匿名子类的实例化。因为我们不能在quasikote中直接使用泛型类型T
,我们首先需要类型的树表示,我们可以通过val tpe=value.tree.tpe
现在,我们的Quasikote如下所示:
q"""
new VarRef[$tpe] {
def get(): $tpe = $value
def set(x: $tpe): Unit = {
$value = x
}
}
"""
trait ValRef[T] {
def get(): T
}
trait VarRef[T] extends ValRef[T] {
def set(x: T): Unit
}
object PointerMacro {
import scala.language.experimental.macros
def pointer[T: c.WeakTypeTag](c: scala.reflect.macros.blackbox.Context)(value: c.Expr[T]) = {
import c.universe._
val symbol: TermSymbol = value.tree.symbol.asTerm
val tpe = value.tree.tpe
if(symbol.isVar || symbol.setter != NoSymbol) {
q"""
new VarRef[$tpe] {
def get(): $tpe = $value
def set(x: $tpe): Unit = {
$value = x
}
}
"""
} else {
q"""
new ValRef[$tpe] {
def get(): $tpe = $value
}
"""
}
}
def &[T](value: T): ValRef[T] = macro pointer[T]
}
只要我们只创建指向var
引用的指针,这个实现就应该有效。但是,只要我们创建一个指向val
引用的指针,编译就会失败,因为“重新分配到val”。因此,我们的宏观经济需要区分两者
显然,提供这种信息。我们希望只为引用创建指针,引用应该提供一个termsymble
val symbol: TermSymbol = value.tree.symbol.asTerm
现在,TermSymbol
api为我们提供了方法isVal
和isVar
,但它们似乎只适用于局部变量。我不确定发现引用是var
还是val
的“正确方法”是什么,但以下方法似乎有效:
if(symbol.isVar || symbol.setter != NoSymbol) {
诀窍在于,限定名称的符号似乎提供了一个setter
symboliff它们是var
引用。否则,setter
返回NoSymbol
val symbol: TermSymbol = value.tree.symbol.asTerm
因此,宏代码如下所示:
q"""
new VarRef[$tpe] {
def get(): $tpe = $value
def set(x: $tpe): Unit = {
$value = x
}
}
"""
trait ValRef[T] {
def get(): T
}
trait VarRef[T] extends ValRef[T] {
def set(x: T): Unit
}
object PointerMacro {
import scala.language.experimental.macros
def pointer[T: c.WeakTypeTag](c: scala.reflect.macros.blackbox.Context)(value: c.Expr[T]) = {
import c.universe._
val symbol: TermSymbol = value.tree.symbol.asTerm
val tpe = value.tree.tpe
if(symbol.isVar || symbol.setter != NoSymbol) {
q"""
new VarRef[$tpe] {
def get(): $tpe = $value
def set(x: $tpe): Unit = {
$value = x
}
}
"""
} else {
q"""
new ValRef[$tpe] {
def get(): $tpe = $value
}
"""
}
}
def &[T](value: T): ValRef[T] = macro pointer[T]
}
如果编译此代码并将其添加到项目的类路径中,则应该能够创建如下指针:
case class Foo() {
val v = "whatever"
var u = 100
}
object Example{
import PointerMacro.&
def main(args: Array[String]): Unit = {
val x = new Foo
val mainInt = 90
var mainString = "this is main"
val localValPointer: ValRef[Int] = &(mainInt)
val localVarPointer: VarRef[String] = &(mainString).asInstanceOf[VarRef[String]]
val memberValPointer: ValRef[String] = &(x.v)
val memberVarPointer: VarRef[Int] = &(x.u).asInstanceOf[VarRef[Int]]
println(localValPointer.get())
println(localVarPointer.get())
println(memberValPointer.get())
println(memberVarPointer.get())
localVarPointer.set("Hello World")
println(localVarPointer.get())
memberVarPointer.set(62)
println(memberVarPointer.get())
}
}
在运行时,应该打印哪些内容
90
this is main
whatever
100
Hello World
62
这是一个声明,不是一个问题,问题是这是否可能?代码草图或类似的指针将不胜感激。谢谢。