【问题标题】:Scala macros assign param of deconstructed functionScala宏分配解构函数的参数
【发布时间】:2013-05-03 07:06:54
【问题描述】:

我目前正在玩一些宏,无论如何这可能是个坏主意,但这是我的问题:

我有以下宏:

def using[A <: { def close(): Unit }, B](resource: A)(f: A => B) = macro usingImpl[A, B]

def usingImpl[A <: { def close(): Unit }, B](c: Context)(resource: c.Expr[A])(f: c.Expr[A => B]): c.Expr[B] = {

  import c.universe._


  f.tree match {
    case Function(params, body) =>
      //val ValDef(modifiers, name, tpt, _) = params.head
      c.Expr[B](
        Block(
          List(
            //ValDef(modifiers, name, tpt, resource.tree)
            ValDef(params.head.symbol, resource.tree)
          ),
          body
        )
      )

    case _: Select =>
      reify {
        val res = resource.splice
        try {
          f.splice(res)
        } finally {
          res.close()
        }
      }
  }
}

如果是Select,我只需调用该函数并关闭资源,就可以正常工作。但如果是Function,我想将参数值分配给资源并调用正文。当我使用 ValDef 的已弃用创建者时,需要 SymbolTree,一切正常。如果我使用的是注释掉的 4-args 创建器,我会收到编译器错误,指出值 x$1 不在范围内。当我查看两个版本生成的代码时,它看起来完全一样:

Expr[Int]({
  <synthetic> val x$1: Test.Foo = new Test.this.Foo();
  x$1.bar.+(23)
})

有没有办法简单地使用params.head 并分配一个值?感谢您的帮助!

编辑

我这样调用宏:

object Test extends App {
  import Macros._

  class Foo {
    def close() {}

    def bar = 3
  }

  println(using(new Foo)(_.bar + 3))
}

正如我所说,如果我使用的是注释掉的版本,它会给我一个编译器错误,它会打印 AST,最后会显示以下消息:[error] symbol value x$1 does not exist in Test$delayedInit$body.apply

我使用的是 2.10.1。

【问题讨论】:

  • 解构params.head 应该可以很好地工作——我无法重现您的错误(无论是 2.10.0 还是 2.10.1),实际上在未注释的版本中会出现编译器错误。你能发布你用来调用宏的代码吗?
  • 您一直在尝试将析构函数放入 Scala 中?好主意!
  • 不,我试图解构一个闭包,以便在运行时它不会创建一个闭包。

标签: scala scala-macros


【解决方案1】:

啊,现在我可以重现您的错误了。任何时候你得到“这个条目似乎已经杀死了编译器”。消息并在堆栈跟踪中看到这样的行:

at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49)

您的下一步应该是开始在c.resetAllAttrs 中包装内容。老实说,我第一次无法重现您的错误的原因是因为在复制和粘贴您的代码之后,我将块中的body 替换为c.resetAllAttrs(body),甚至没有考虑它。这只是一种反射。

请参阅 this little single abstract method class demo 以了解需要此技巧的地方的类似实例。

在你的情况下,如果我们有这个:

import scala.language.experimental.macros
import scala.reflect.macros.Context

object Macros {
  def using[A <: { def close(): Unit }, B](resource: A)(f: A => B) =
    macro usingImpl[A, B]

  def usingImpl[A <: { def close(): Unit }, B](c: Context)
    (resource: c.Expr[A])(f: c.Expr[A => B]): c.Expr[B] = {
    import c.universe._

    val expr = f.tree match {
      case Function(ValDef(modifiers, name, tpt, _) :: Nil, body) =>
        c.Expr[B](
          Block(
            ValDef(modifiers, name, tpt, resource.tree) :: Nil,
            c.resetAllAttrs(body)
          )
        )

      case _: Select => reify {
        val res = resource.splice
        try { f.splice(res) } finally { res.close() }
      }
    }

    println(expr)
    expr
  }
}

当我们编译您的测试代码时,我们会看到以下内容:

Expr[B]({
  <synthetic> val x$1: $line3.$read.$iw.$iw.Test.Foo = new Test.this.Foo();
  x$1.bar.$plus(3)
})

完全符合要求。

【讨论】:

    猜你喜欢
    • 2013-06-14
    • 2013-11-16
    • 2013-09-08
    • 2017-06-14
    • 1970-01-01
    • 2013-11-07
    • 2013-03-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多