Scala Compiler Plugin Rewrite Function Definition As A Tuple: error: not found: value scala.Tuple2

Scala Compiler Plugin Rewrite Function Definition As A Tuple: error: not found: value scala.Tuple2

我正在编写一个编译器插件来将函数定义重写为函数散列 + 函数体的元组

所以下面

def f(a: Int, b: Int) : Int = (a + b) 

会变成

val f = ("some-complex-hash", (a: Int, b: Int) => (a + b))

请注意,这是一个研究项目,将用于将可逆计算的某些变体集成到该语言的一个子集中。我知道,就其本身而言,这是一个坏主意,会破坏很多东西。

构建编译器插件的文档似乎比较缺乏(我确实看过官方指南),所以我正在努力寻找现有的插件,例如kind-projector

为了理解如何表示这个,我遵循了以下过程

  1. 具体化表达式 val expr = reify {....}
  2. 提取树 val tree = expr.tree
  3. showRaw(tree)

我已经为一个函数定义、元组和一个 lambda 做了这个,我相信这应该足以实现它。到目前为止我得到了以下信息:

ValDef(Modifiers(), TermName(dd.name), TypeTree(),
   Apply(
       Select(Ident("scala.Tuple2"), TermName("apply")),
          List(
              Literal(Constant(hash)),
              Function(
                  List(dd.vparamss),
                  dd.rhs
              )
          )
    )
 )

在我开始之前,我在扩展到 任何元组 时遇到了麻烦,即将任何函数重写为 ("a", "b") 在 REPL

中扩展为以下内容
Apply(Select(Ident(scala.Tuple2), TermName("apply")), List(Literal(Constant("a")), Literal(Constant("b"))))

问题

如果我这样做 Ident(scala.Tuple2) 我会在 编译时得到以下结果

overloaded method value Ident with alternatives:
[error]   (sym: FunctionRewriter.this.global.Symbol)FunctionRewriter.this.global.Ident <and>
[error]   (name: String)FunctionRewriter.this.global.Ident <and>
[error]   FunctionRewriter.this.global.Ident.type
[error]  cannot be applied to (Tuple2.type)
[error]           Select(Ident(scala.Tuple2), TermName("apply")),

如果我做 Ident("scala.Tuple2")(注意字符串),当插入 运行s(在“运行 时间”)

时,我会得到以下结果
<test>:2: error: not found: value scala.Tuple2
[error] object Main extends App {

对于如何重写为元组的任何指示,我将不胜感激

完整代码:

class CompilerPlugin(override val global: Global) extends Plugin {
  val name        = "provenance-expander"
  val components  = new FunctionRewriter(global) :: Nil
}

class FunctionRewriter(val global: Global) extends PluginComponent with TypingTransformers {
  import global._
  override val phaseName = "compiler-plugin-phase"
  override val runsAfter = List("parser")
  override def newPhase(prev: Phase) = new StdPhase(prev) {
    override def apply(unit: CompilationUnit) {
      unit.body = new MyTypingTransformer(unit).transform(unit.body)
    }
  }

  class MyTypingTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {

    override def transform(tree: Tree) : Tree = tree match {
      case dd: DefDef =>
        val hash: String = "do some complex recursive hashing" 
        Apply(
          Select(Ident("scala.Tuple2"), TermName("apply")),
          List(Literal(Constant("a")), Literal(Constant("b")))
        )
      case _ => super.transform(tree)
    }
  }

  def newTransformer(unit: CompilationUnit) = new MyTypingTransformer(unit)
}

感谢@SethTisue 在评论中的回答。我正在为将来可能遇到类似问题的任何人写一个答案。

正如 Seth 提到的,使用 mkTuple 是正确的方法。为了使用它,您需要以下导入

import global.gen._

在这种特定情况下,正如最初在问题中推测的那样,转换破坏了很多东西。主要转换对象注入的方法和 class 定义,即方法分派或初始化,导致格式错误的结构。解决方法是使用显式注释。所以最后的 DefDef 最终看起来像下面这样:

case dd: DefDef =>
    if (dd.mods.hasAnnotationNamed(TypeName(typeOf[policyFunction].typeSymbol.name.toString))) {
      val hash: String = md5HashString(dd.rhs.toString())
      val tup =
        atPos(tree.pos.makeTransparent)(mkTuple(List(Literal(Constant(hash)), Function(dd.vparamss(0), dd.rhs))))
      val q = ValDef(Modifiers(), dd.name, TypeTree(), tup)
      println(s"Re-written Function: $q")
      q
    } else {
      dd
    }