问题
Does anyone know how to get the (Scala part only) desugared translation of a for/comprehension expression before it actually tries to compile in the REPL (or compiler)?
The only thing I've found so far is the compiler "-print" flag but that gives you the full Scala translation…
回答1:
As I already said in the other topic, scalac -print
prints out scala code, not java. It translates all scala keywords that are not directly compatible with java to normal scala code. It is not possible to let the compiler translate only parts afaik. But basically a for-comprehension is always translated the same way.
A simple for/yield like this
for(x <- List(1,2,3)) yield x*x
will be translated to
List(1,2,3).map {x => x*x}
And without yield
for(x <- List(1,2,3)) println(x)
to
List(1,2,3).foreach{x => println(x)}
Nested fors will be translated to nested flatMap/map constructs
for(x <- List(1,2,3); y <- List(4,5,6)) yield x*y
will be translated to
List(1,2,3).flatMap { x =>
List(4,5,6).map { y =>
x*y
}
}
So there is absolutely no magic
回答2:
It doesn't seem to exists any possibilities to desugar "for/comprehension" expressions directly within the REPL. But as an alternative one can use some Scala compiler options like "-print" or for simple expressions "Xprint:typer -e"
Example:
To get the desugard output from a file use the "-print" flag:
# scala -print file.scala
To desugar a simple one-liner expression, use the "-Xprint:typer -e" flag:
# scala -Xprint:typer -e "for (i <- 0 to 100) yield i"
回答3:
How about a 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
This can be used directly in the REPL, as per your request:
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])
It also works on other arbitrary expressions.
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))
}
This is probably the closest you'll get to what you're asking without ever having to compile or dump the data out to a file at any point. You can define the macro directly in the REPL, or in an external file loaded with the :load
command.
回答4:
To see the result after simple desugaring use the -Xprint:parser
option.
If you have this simple input file named test.scala
:
object Test {
for(x <- List(1,2,3); y <- List(4,5,6)) yield x*y
}
Then compiling it using scalac -Xprint:parser
prints out:
$ 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)))))
}
}
To get a complete list of compiler phases applicable to -Xprint:<phase>
do this:
$ 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
The -Xprint:<phase>
option is also applicable to scala
and thus to the REPL. However, you will see all the wrapper code the REPL inserts as well.
$ 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>
回答5:
Intellij has a feature called "Explain Scala" that does a LOT of desugaring including expanding for comprehensions into map/flatMap/filter directly in the file you are editing.
Please note that since IntelliJ 2017.1 this is now called "Desugar Scala Code" and is in the "Code" menu (thanks Mikaël for the info).
回答6:
In scala 2.11, it is also possible to use 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)
来源:https://stackoverflow.com/questions/9891407/getting-the-desugared-part-of-a-scala-for-comprehension-expression