【问题标题】:How to transform and apply a partial function using Scala Macros?如何使用 Scala 宏转换和应用部分函数?
【发布时间】:2013-08-26 10:34:41
【问题描述】:

我想实现一个 Scala 宏,它接受一个部分函数,​​对函数的模式执行一些转换,然后将其应用于给定的表达式。

为此,我从以下代码开始:

def myMatchImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(expr: c.Expr[A])(patterns: c.Expr[PartialFunction[A, B]]): c.Expr[B] = {
  import c.universe._

  /*
   * Deconstruct the partial function and select the relevant case definitions.
   * 
   * A partial, anonymus function will be translated into a new class of the following form:
   * 
   * { @SerialVersionUID(0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[A,B] with Serializable {
   * 
   *     def <init>(): anonymous class $anonfun = ...
   *
   *     final override def applyOrElse[...](x1: ..., default: ...): ... = ... match {
   *       case ... => ...
   *       case (defaultCase$ @ _) => default.apply(x1)
   *     }
   *
   *     def isDefined ...
   *   }
   *   new $anonfun()
   * }: PartialFunction[A,B]
   *
   */
  val Typed(Block(List(ClassDef(a, b, x, Template(d, e, List(f, DefDef(g, h, i, j, k, Match(l, allCaseDefs)), m)))), n), o) = patterns.tree

  /* Perform transformation on all cases */
  val transformedCaseDefs: List[CaseDef] = allCaseDefs map {
    case caseDef => caseDef // This code will perform the desired transformations, now it's just identity
  }

  /* Construct anonymus partial function with transformed case patterns */
  val result = Typed(Block(List(ClassDef(a, b, x, Template(d, e, List(f, DefDef(g, h, i, j, k, Match(l, transformedCaseDefs)), m)))), n), o)
  // println(show(result))

  c.Expr[B](q"$result($expr)")
}

我解构部分函数,​​选择 applyOrElse 函数的案例定义,对每个定义执行所需的转换,然后将所有内容重新组合在一起。宏是这样调用的:

def myMatch[A, B](expr: A)(patterns: PartialFunction[A, B]): B = macro myMatchImpl[A,B]

很遗憾,这并没有按预期工作。在一个简单的例子中使用宏

def test(x: Option[Int]) = myMatch(x){
  case Some(n) => n
  case None    => 0
}

导致以下错误消息:

object creation impossible, since method isDefinedAt in trait PartialFunction of type (x: Option[Int])Boolean is not defined

这有点令人困惑,因为打印生成的部分函数会产生

({
  final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[Option[Int],Int] with Serializable {
    def <init>(): anonymous class $anonfun = {
      $anonfun.super.<init>();
      ()
    };
    final override def applyOrElse[A1 <: Option[Int], B1 >: Int](x2: A1, default: A1 => B1): B1 = ((x2.asInstanceOf[Option[Int]]: Option[Int]): Option[Int] @unchecked) match {
      case (x: Int)Some[Int]((n @ _)) => n
      case scala.None => 0
    };
    final def isDefinedAt(x2: Option[Int]): Boolean = ((x2.asInstanceOf[Option[Int]]: Option[Int]): Option[Int] @unchecked) match {
      case (x: Int)Some[Int]((n @ _)) => true
      case scala.None => true
      case (defaultCase$ @ _) => false
    }
  };
  new $anonfun()
}: PartialFunction[Option[Int],Int])

其中明确定义了 isDefinedAt 方法。

有没有人有想法,这里有什么问题以及如何正确地做到这一点?

【问题讨论】:

  • 这不是您问题中最有趣部分的答案,但您是否尝试过使用Transformer?它应该do what you want,可能更健壮。
  • @TravisBrown Transformer 类正是我想要的,谢谢你的提示。随时发布您的要点作为答案。

标签: scala macros scala-2.10 scala-macros


【解决方案1】:

新的反射 API 提供了一个 Transformer 类,专门用于帮助进行这种树转换:

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

def myMatchImpl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(
  expr: c.Expr[A]
)(
  patterns: c.Expr[PartialFunction[A, B]]
): c.Expr[B] = {
  import c.universe._

  val transformer = new Transformer {
    override def transformCaseDefs(trees: List[CaseDef]) = trees.map {
      case caseDef => caseDef
    }
  }

  c.Expr[B](q"${transformer.transform(patterns.tree)}($expr)")
}

def myMatch[A, B](expr: A)(patterns: PartialFunction[A, B]): B =
  macro myMatchImpl[A,B]

def test(x: Option[Int]) = myMatch(x) {
  case Some(n) => n
  case None    => 0
}

您可能需要一些额外的机制来确保仅将转换应用于您希望将其应用于的案例列表,但通常这种方法比手动转换树更可靠。

不过,我仍然很好奇为什么您的版本不起作用,如果您有时间,可能值得在这里为另一个问题整理一个简化示例。

【讨论】:

  • 我猜在宏扩展中混合有类型和无类型的树会有一些令人讨厌的副作用。部分函数合成调用了相当一部分的魔法,在部分输入的情况下可能会出错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-02
相关资源
最近更新 更多