El problema es que el código mezcla los conceptos de tiempo de compilación y tiempo de ejecución.
La variable "list" que está utilizando es un valor en tiempo de compilación (es decir, se supone que se iterará durante el tiempo de compilación), y está pidiendo reify para retenerlo hasta el tiempo de ejecución (mediante empalme derivado valores). Este enigma de etapas cruzadas conduce a la creación del llamado término libre.
En resumen, los términos libres son los resguardos que se refieren a los valores de las etapas anteriores. Por ejemplo, el siguiente fragmento:
val x = 2
reify(x)
se compilará como sigue:
val free$x1 = newFreeTerm("x", staticClass("scala.Int").asTypeConstructor, x);
Ident(free$x1)
inteligente, ¿eh? El resultado conserva el hecho de que x es un identificador, conserva su tipo (características de tiempo de compilación), pero, sin embargo, se refiere a su valor también (una característica de tiempo de ejecución). Esto es posible gracias al alcance léxico.
Pero si intenta devolver este árbol desde una expansión de macros (que está incluido en el sitio de llamadas de una macro), las cosas explotarán. El sitio de llamada de la macro probablemente no tenga x en su alcance léxico, por lo que no podría referirse al valor de x.
Lo que es aún peor. Si el fragmento anterior está escrito dentro de una macro, entonces x solo existe durante el tiempo de compilación, es decir, en la JVM que ejecuta el compilador. Pero cuando el compilador termina, ya no está.
Sin embargo, se supone que los resultados de la expansión de macro que contienen una referencia a x se ejecutan en tiempo de ejecución (lo más probable es que en una JVM diferente). Para darle sentido a esto, necesitaría persistencia en varias etapas, es decir, una capacidad para serializar de algún modo valores de tiempo de compilación arbitrarios y deserializarlos durante el tiempo de ejecución. No sé cómo hacer esto en un lenguaje compilado como Scala.
Tenga en cuenta que, en algunos casos, es posible la persistencia en etapas cruzadas.Por ejemplo, si x era un campo de un objeto estático:
object Foo { val x = 2 }
import Foo._
reify(x)
entonces no sería terminar como un término libre, pero se cosifica de una manera sencilla:
Select(Ident(staticModule("Foo")), newTermName("x"))
Este es un concepto interesante que también se discutió en la charla de SPJ en Scala Days 2012: http://skillsmatter.com/podcast/scala/haskell-cloud.
Para verificar que alguna expresión no contiene términos libres, en Haskell agregan una nueva primitiva incorporada al compilador, el constructor de tipo Static
. Con las macros, podemos hacer esto de forma natural mediante el uso de reify (que en sí mismo es solo una macro). Vea la discusión aquí: https://groups.google.com/forum/#!topic/scala-internals/-42PWNkQJNA.
Bien, ahora hemos visto cuál es exactamente el problema con el código original, entonces, ¿cómo lo hacemos funcionar?
Lamentablemente tendremos que recurrir a la construcción manual de AST, porque reify tiene dificultades para expresar árboles dinámicos. El caso de uso ideal para reify en macrology es tener una plantilla estática con los tipos de agujeros conocidos en el momento de la macro compilación. Haga un paso a un lado, y tendrá que recurrir a la construcción de árboles a mano.
conclusión es que hay que ir con la siguiente (funciona con recientemente publicado 2.10.0-M4, consulte la guía de migración en Scala-idioma para ver qué es exactamente lo que ha cambiado: http://groups.google.com/group/scala-language/browse_thread/thread/bf079865ad42249c):
import scala.reflect.makro.Context
object Macros {
def join_impl(c: Context)(a: c.Expr[Int]): c.Expr[List[Int]] = {
import c.universe._
import definitions._
a.tree match {
case Block(list, ret) =>
c.Expr((list :+ ret).foldRight(Ident(NilModule): Tree)((el, acc) =>
Apply(Select(acc, newTermName("$colon$colon")), List(el))))
}
}
def join(a: Int): List[Int] = macro join_impl
}
¿Habrá algo más fácil en scala 2.11? ¿Las cuasi citas van a ayudar? (No he estudiado macros en profundidad, pero cada vez que lo intento, sigo tocándome con esto) – HRJ
Quasiquotes me ayudarán un poco: https://gist.github.com/densh/6209261 –