在传递给 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
}
})
即使将处理函数参数的函数传递给宏也无法编译。 示例如下所示。
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
}
})