在传递给 scala 宏的函数中,我无法在范围内引用变量

Within a function passed to a scala macro, I cannot reference a variable in scope

即使将处理函数参数的函数传递给宏也无法编译。 示例如下所示。

trait Generated[Z] {
  def deserialize[A](t: A): Z
}

def from[A, Z](apl: A => Z): Generated[Z] = macro GeneratorMacro.from[A, Z]

class GeneratorMacro(val c: blackbox.Context) {
  import c.universe._
  def from[A: c.WeakTypeTag, Z: c.WeakTypeTag](apl: c.Expr[A => Z]): c.Expr[Generated[Z]] = {
    reify {
      new Generated[Z] {
        def deserialize[A](t: A): Z = {
          apl.splice.apply(t)
        }
      }
    }
  }
}
object Generation {
  def apply(input: String): Generated[Int] = {
    Generator.from[Option[String], Int] {
      case Some(i) => input.toInt + i.toInt // compilation failed
      case None => 0
    }
  }
}

此时出现错误

Error: scalac: Error while emitting Generation.scala
value input

class不是用宏展开重新编译了吗? 如果用内联扩展重新编译,应该不会出现编译错误。

object Generation {
  def apply(input: String): Generated[Int] = {
    new Generated[Int] {
      def deserialize(t: String): Int = {
        {
          case Some(i) => input.toInt + i.toInt // input should be visible
          case None => 0
        }.apply(t)
      }
    }
  }
}

这是怎么回事,如何避免。

这似乎是不可能的,AFAICS。编译器为您的代码创建一个匿名的 class,但它不会捕获其中宏调用的词法上下文。

代码在 lambdalift 阶段之后看起来有点像这样:

def apply(input: String): Generated[Int] = ({
      new <$anon: Generated>()
    }: Generated);

...

final class $anon extends Object with Generated {
  def deserialize(t: Option): Int = {
    ... // <- your code is here !!
  };
}

当然,代码在这个地方无法访问input变量。

这可能是 Scala 编译器中的错误...

我遇到的第一个错误是

Warning:scalac: {
  final class $anon extends App.this.Generated[Int] {
    def <init>() = {
      super.<init>();
      ()
    };
    def deserialize(t: Option[String]): Int = ((x0: Option[String]) => x0 match {
  case (value: String)Some[String]((i @ _)) => scala.Predef.augmentString(input).toInt.+(scala.Predef.augmentString(i).toInt)
  case scala.None => 0
}).apply(t)
  };
  new $anon()
}

Error:(6, 43) object creation impossible. Missing implementation for:
  def deserialize[A](t: A): Int // inherited from trait Generated
      Generator.from[Option[String], Int] {

显然这是因为你定义了

reify {
  new Generated[Z] {
    def deserialize(t: A): Z = {
      ...

而不是 def deserialize[A](t: A): Z@OlegPyzhcov 在评论中指出了这一点)。

关于你的错误 Error while emitting ... 问题是

{
  case Some(i: String) => input.toInt + i.toInt
  case None => 0
}

的类型不是 ... => ...Function1[..., ...] 但实际上是 PartialFunction[..., ...].

试试看

object Generator {
  def from[Z](apl: PartialFunction[Any, Z]): Generated[Z] = macro GeneratorMacro.from[Z]
}

class GeneratorMacro(val c: blackbox.Context) {
  import c.universe._
  def from[Z: c.WeakTypeTag](apl: c.Expr[Any => Z]): c.Expr[Generated[Z]] = {
    reify {
      new Generated[Z] {
        def deserialize[A](t: A): Z = {
          apl.splice.apply(t)
        }
      }
    }
  }
}

Generator.from[Int]({
  case Some(i: String) => input.toInt + i.toInt
  case None => 0
})

object Generator {
  def from[Z](apl: Any => Z): Generated[Z] = macro GeneratorMacro.from[Z]
}

class GeneratorMacro(val c: blackbox.Context) {
  import c.universe._
  def from[Z: c.WeakTypeTag](apl: c.Expr[Any => Z]): c.Expr[Generated[Z]] = {
    reify {
      new Generated[Z] {
        def deserialize[A](t: A): Z = {
          apl.splice.apply(t)
        }
      }
    }
  }
}

Generator.from[Int](new (Any => Int) {
  override def apply(x: Any): Int = x match {
    case Some(i: String) => input.toInt + i.toInt
    case None => 0
  }
})