Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala宏创建字段和方法指针_Scala_Scala Macros - Fatal编程技术网

Scala宏创建字段和方法指针

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

我想创建一个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): 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

  • 如果您只对代码感兴趣,请滚动至答案的底部
请注意,宏是在编译时执行的,因此必须进行预编译。如果您使用的是IntelliJ(或Eclipse),那么我建议将所有与宏相关的代码放在一个单独的项目中

正如你提到的,我们有两个指针特性

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

这是一个声明,不是一个问题,问题是这是否可能?代码草图或类似的指针将不胜感激。谢谢。