这个def宏有什么问题?

时间:2014-08-22 14:42:09

标签: scala macros scala-macros

免责声明:此代码不具有实际用途,仅用于教育目的。

tl; dr:大部分只是我试图调试问题的结果,所以只有前3个片段很重要。

这是宏定义:

def tx[T](ds: GraphDatabaseService)(block: => T): Option[T] = 
  macro txmacros.blockTxImpl[T]

以下是实施:

def blockTxImpl[T: c.WeakTypeTag](c: whitebox.Context)(ds: c.Tree)(block: c.Tree):
    c.Tree = {
  import c.universe._

  q"""
    val tx = $ds.beginTx()
    val newRetVal = try {
      val retVal = {
        $block
      }
      tx.success()
      Option(retVal)
    } catch {
      case _ : Throwable =>
        tx.failure()
        None
    } finally {
      tx.close()
    }
    newRetVal
  """
}

以下是它的名称:

val nodePropK5 = tx(db) {
  // simplified for brevity
  val node = db.find(label, "key", 100).iterator().next()
  node.getProperty("k5", 300)
}

nodePropK5 should be (Some(200))

整个项目可以找到https://github.com/cdshines/txMacro/(准备建立一个跑步)。

这样的调用失败,并显示以下消息:

[error] symbol value node does not exist in MacroTest$$anonfun$3.apply$mcV$sp
[trace] Stack trace suppressed: run last core/test:compile for the full output.
[error] (core/test:compile) scala.reflect.internal.FatalError: symbol value node does not exist in MacroTest$$anonfun$3.apply$mcV$sp

但是,如果我将有问题的代码更改为

val nodePropK5 = tx(db) {
  db.findNodesByLabelAndProperty(label, "k4", 100).iterator().next().getProperty("k5", 300)
}

返回值为Some(300),符合预期。添加不声明新变量的行(或使用node)不会破坏行为,而

val nodePropK5 = tx(db) {
  db.findNodesByLabelAndProperty(label, "k4", 100).iterator().next().getProperty("k5", 300)
  val x = 5
  x
}

会产生相同的信息。

另一个特殊的事情:如果我在宏扩展期间打印失败的block,我会得到以下代码:

{
  val tx = MacroTest.this.db.beginTx();
  val newRetVal = try {
    val retVal = {
      val node: org.neo4j.graphdb.Node = MacroTest.this.db.findNodesByLabelAndProperty(MacroTest.this.label, "k4", 100).iterator().next();
      node.getProperty("k5", 300)
    };
    tx.success();
    Option(retVal)
  } catch {
    case (_: Throwable) => {
      tx.failure();
      None
    }
  } finally tx.close();
  newRetVal
}

其中,手动替换,效果很好。

我在这里缺少什么?我可以自由地假设这是一个编译器错误吗?

1 个答案:

答案 0 :(得分:2)

当你看到这种错误时,首先要尝试的是在宏输出中“重复使用”的非类型检查代码。在这种情况下,替换以下内容:

val retVal = {
  $block
}

使用:

val retVal = {
  ${ c.untypecheck(block) }
}

应该做的伎俩。

请注意,在2.10中,untypecheck的等效值为resetAllAttrs / resetLocalAttrs。如果你搜索这些名字,你会发现很多关于你所看到的问题细节的讨论。