Scala Compiler Plugin Rewrite Función Definición Como Tuple: error: no se encuentra: valor scala. Tuple2

Estoy escribiendo un plugin compilador para reescribir una definición de función como un tuple de la función hash + cuerpo de función

Así que lo siguiente

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

se convertiría en

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

Permítaseme señalar que esto es para un proyecto de investigación y se utilizará para integrar alguna variante de computaciones reversibles en un subconjunto del lenguaje. Soy consciente de que, por su cuenta, esta es una mala idea y romperá muchas cosas.

La documentación del enchufe del compilador en la construcción parece ser bastante falta (pasé por la guía oficial), así que estoy tratando de avanzar mirando los plugins existentes como los proyector

Para entender cómo representar esto, he seguido el siguiente proceso

  1. Reify the expression val expr = reify {....}
  2. Extraiga el árbol val tree = expr.tree
  3. showRaw(tree)

He hecho esto para una definición de función, tuple y una lambda, que creo que debe ser suficiente para implementar esto. Tengo lo siguiente hasta ahora:

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

Antes de llegar a esto, estoy teniendo problemas con expandirme cualquier tuple en todo i.e. reescribir cualquier función como ("a", "b") que se expande a lo siguiente en la REPL

Apply(Select(Ident(scala.Tuple2), TermName("apply")), List(Literal(Constant("a")), Literal(Constant("b"))))

El problema

Si lo hago Ident(scala.Tuple2) Tengo lo siguiente tiempo de compilación

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

Si lo hago Ident("scala.Tuple2") (conozca la cuerda), obtengo lo siguiente cuando el enchufe se ejecuta (en "tiempo de ejecución")

:2: error: not found: value scala.Tuple2
[error] object Main extends App {

Agradecería cualquier puntero sobre cómo reescribir como Tuples

El Código completo:

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)
}

Pregunta hecha hace 3 años, 4 meses, 26 días - Por byteexplorer


3 Respuestas:

  • Gracias a @SethTisue por responder en los comentarios. Estoy escribiendo una respuesta para cualquiera que pueda enfrentar un problema similar en el futuro.

    Como Seth mencionó, usando mkTuple era la manera correcta de ir. Para utilizarlo, necesita la siguiente importación

    import global.gen._
    

    En este caso específico, como originalmente especulado en la pregunta, la transformación rompe muchas cosas. Transformando principalmente los métodos inyectados por las definiciones de objeto y clase, es decir, para el envío de métodos o init, resulta en estructuras malformadas. El trabajo alrededor está usando anotaciones explícitas. Así que la final DefDef termina mirando como lo siguiente:

    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
        }
    

    Respondida el Dec 19, 2020 a las 16:39 - por bytebard66b1

    Votos positivos: 0 | Votos negativos: 0

  • El error que estás recibiendo se debe a que estás tratando de usar scala.Tuple2 como un valor en lugar de como un tipo. Para solucionar este problema, necesitas importar Tuple2 en el contexto de tu plugin compilador.

    Aquí te dejo una guía paso a paso para corregir el error:

    • Agrega una importación de `scala.Tuple2` al principio de tu archivo:
    scala
    import scala.Tuple2
    
    • Utiliza `Tuple2` como un tipo en lugar de como un valor:
    scala
    Apply(
      Select(Ident(Tuple2), TermName("apply")),
      List(
        Literal(Constant(hash)),
        Function(
          List(dd.vparamss),
          dd.rhs
        )
      )
    )
    

    Con estas correcciones, el error relacionado con scala.Tuple2 debería solucionarse y podrás continuar con la implementación de tu plugin compilador para reescribir las definiciones de función como tuplas según lo que describiste en tu pregunta.

    Espero que esta información te sea de ayuda. ¡Déjame saber si tienes alguna otra pregunta o duda!

    Respondida el Dec 20, 2020 a las 16:38 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • El problema es que estás intentando acceder al valor scala.Tuple2 directamente, que está definido en el paquete scala, pero no lo estás importando. Para resolver este problema, debes importar el paquete scala en tu código.

    Aquí tienes tu código corregido:

    scala
    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._
      import scala._ // Import the scala package
    
      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("Tuple2"), TermName("apply")), // Use Ident("Tuple2") instead of Ident("scala.Tuple2")
              List(Literal(Constant("a")), Literal(Constant("b")))
            )
          case _ => super.transform(tree)
        }
      }
    
      def newTransformer(unit: CompilationUnit) = new MyTypingTransformer(unit)
    }
    

    Espero que esto solucione tu problema. Por favor, házmelo saber si tienes más preguntas.

    Respondida el Dec 20, 2020 a las 16:45 - por Gemini

    Votos positivos: 0 | Votos negativos: 0