¿Conseguir la parte desugared de un Scala para la expresión de comprensión?
syntactic-sugar for-comprehension (6)
¿Qué tal una macro?
import scala.reflect.macros.Context
import scala.reflect.runtime.universe._
import scala.language.experimental.macros
def _desugar(c : Context)(expr : c.Expr[Any]): c.Expr[Unit] = {
import c.universe._
println(show(expr.tree))
reify {}
}
def desugar(expr : Any) = macro _desugar
Esto se puede utilizar directamente en el REPL, según su solicitud:
scala> desugar { for(i <- List(1,2,3,4,5)) yield i }
immutable.this.List.apply[Int](1, 2, 3, 4, 5).map[Int, Any](((i: Int) =>
i))(immutable.this.List.canBuildFrom[Int])
scala> desguar { for(i <- (0 to 10) if (i > 5)) yield i }
scala.this.Predef.intWrapper(0).to(10).withFilter(((i: Int) => i.>(5))).map[Int,
Any](((i: Int) => i))(immutable.this.IndexedSeq.canBuildFrom[Int])
También funciona en otras expresiones arbitrarias.
scala> desugar {
| val x = 20
| val y = 10
| println(x + y)
| }
{
val x: Int = 20;
val y: Int = 10;
scala.this.Predef.println(x.+(y))
}
Es probable que esto sea lo más cercano a lo que está pidiendo sin tener que compilar o volcar los datos en un archivo en ningún momento. Puede definir la macro directamente en el REPL, o en un archivo externo cargado con el comando :load
.
¿Alguien sabe cómo obtener la traducción desugared (solo parte de Scala) de una expresión para / comprensión antes de que realmente intente compilar en el REPL (o compilador)?
Lo único que he encontrado hasta ahora es el indicador "-print" del compilador, pero eso te da la traducción completa de Scala ...
En scala 2.11, también es posible usar quasiquotes :
val universe: scala.reflect.runtime.universe.type = scala.reflect.runtime.universe
import universe._
val tree = q"""
val x = 20
val y = 10
println(x + y)
"""
println(tree)
Intellij tiene una función llamada " Explicar Scala " que hace MUCHO desugaring, incluida la expansión para comprensión en map / flatMap / filter directamente en el archivo que está editando.
Tenga en cuenta que desde IntelliJ 2017.1, ahora se llama "Código de Desugar Scala" y se encuentra en el menú "Código" (gracias a Mikaël por la información).
No parece existir ninguna posibilidad de desugar expresiones "para / comprensión" directamente dentro del REPL. Pero como alternativa, se pueden usar algunas opciones del compilador Scala como "-print" o para expresiones simples "Xprint: typer -e"
Ejemplo:
Para obtener la salida de desugard de un archivo, use la marca "-print":
# scala -print file.scala
Para desugar una simple expresión de una línea, use el indicador "-Xprint: typer -e":
# scala -Xprint:typer -e "for (i <- 0 to 100) yield i"
Para ver el resultado después de una eliminación de fugas simple, use la opción -Xprint:parser
.
Si tiene este simple archivo de entrada llamado test.scala
:
object Test {
for(x <- List(1,2,3); y <- List(4,5,6)) yield x*y
}
Luego compilándolo con scalac -Xprint:parser
imprime:
$ scalac -Xprint:parser test.scala
[[syntax trees at end of parser]] // test.scala
package <empty> {
object Test extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
List(1, 2, 3).flatMap(((x) => List(4, 5, 6).map(((y) => x.$times(y)))))
}
}
Para obtener una lista completa de las fases del compilador aplicables a -Xprint:<phase>
haga esto:
$ scalac -Xshow-phases
phase name id description
---------- -- -----------
parser 1 parse source into ASTs, perform simple desugaring
namer 2 resolve names, attach symbols to named trees
packageobjects 3 load package objects
typer 4 the meat and potatoes: type the trees
patmat 5 translate match expressions
superaccessors 6 add super accessors in traits and nested classes
extmethods 7 add extension methods for inline classes
pickler 8 serialize symbol tables
refchecks 9 reference/override checking, translate nested objects
selectiveanf 10
selectivecps 11
uncurry 12 uncurry, translate function values to anonymous classes
tailcalls 13 replace tail calls by jumps
specialize 14 @specialized-driven class and method specialization
explicitouter 15 this refs to outer pointers, translate patterns
erasure 16 erase types, add interfaces for traits
posterasure 17 clean up erased inline classes
lazyvals 18 allocate bitmaps, translate lazy vals into lazified defs
lambdalift 19 move nested functions to top level
constructors 20 move field definitions into constructors
flatten 21 eliminate inner classes
mixin 22 mixin composition
cleanup 23 platform-specific cleanups, generate reflective calls
icode 24 generate portable intermediate code
inliner 25 optimization: do inlining
inlineExceptionHandlers 26 optimization: inline exception handlers
closelim 27 optimization: eliminate uncalled closures
dce 28 optimization: eliminate dead code
jvm 29 generate JVM bytecode
terminal 30 The last phase in the compiler chain
La -Xprint:<phase>
también es aplicable a scala
y, por lo tanto, a REPL. Sin embargo, también verá todo el código de envoltorio que inserta el REPL.
$ scala -Xprint:parser
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.
<..a lot of initialisation code printed..>
scala> object Test {
| for(x <- List(1,2,3); y <- List(4,5,6)) yield x*y
| }
[[syntax trees at end of parser]] // <console>
package $line3 {
object $read extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
object $iw extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
object $iw extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
object Test extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
List(1, 2, 3).flatMap(((x) => List(4, 5, 6).map(((y) => x.$times(y)))))
}
}
}
}
}
[[syntax trees at end of parser]] // <console>
package $line3 {
object $eval extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
lazy val $result = $line3.$read.$iw.$iw.Test;
val $print: String = {
$read.$iw.$iw;
"".$plus("defined module ").$plus("Test").$plus("/n")
}
}
}
defined module Test
scala>
Como ya dije en el otro tema, scalac -print
imprime el código de scalac -print
, no Java. Traduce todas las palabras clave de Scala que no son directamente compatibles con Java al código de Scala normal. No es posible dejar que el compilador traduzca solo partes de afaik. Pero básicamente una comprensión para siempre se traduce de la misma manera.
Un simple para / rendimiento como este
for(x <- List(1,2,3)) yield x*x
será traducido a
List(1,2,3).map {x => x*x}
Y sin rendimiento
for(x <- List(1,2,3)) println(x)
a
List(1,2,3).foreach{x => println(x)}
Los fors anidados se traducirán a construcciones planas / mapas anidadas
for(x <- List(1,2,3); y <- List(4,5,6)) yield x*y
será traducido a
List(1,2,3).flatMap { x =>
List(4,5,6).map { y =>
x*y
}
}
Así que no hay absolutamente ninguna magia.