REPL (またはコンパイラ) で実際にコンパイルしようとする前に、for/comprehension 式の (Scala 部分のみ) desugar 変換を取得する方法を知っている人はいますか?
私がこれまでに見つけた唯一のものは、コンパイラの「-print」フラグですが、これで Scala の完全な翻訳が得られます…
REPL (またはコンパイラ) で実際にコンパイルしようとする前に、for/comprehension 式の (Scala 部分のみ) desugar 変換を取得する方法を知っている人はいますか?
私がこれまでに見つけた唯一のものは、コンパイラの「-print」フラグですが、これで Scala の完全な翻訳が得られます…
他のトピックで既に述べたように、scalac -print
java ではなく scala コードを出力します。Java と直接互換性がないすべての scala キーワードを通常の scala コードに変換します。コンパイラーに一部のみを翻訳させることはできません。しかし、基本的には for-comprehension は常に同じように翻訳されます。
このような単純な for/yield
for(x <- List(1,2,3)) yield x*x
に翻訳されます
List(1,2,3).map {x => x*x}
そして、譲歩せずに
for(x <- List(1,2,3)) println(x)
に
List(1,2,3).foreach{x => println(x)}
ネストされた for は、ネストされた flatMap/map 構造に変換されます
for(x <- List(1,2,3); y <- List(4,5,6)) yield x*y
に翻訳されます
List(1,2,3).flatMap { x =>
List(4,5,6).map { y =>
x*y
}
}
だから魔法は絶対にない
REPL内で直接「for/comprehension」式を脱糖する可能性はないようです。しかし、別の方法として、「-print」や単純な式「Xprint:typer -e」などの Scala コンパイラ オプションを使用できます。
例:
ファイルから desugard 出力を取得するには、「-print」フラグを使用します。
# scala -print file.scala
単純なワンライナー式を desugar するには、「-Xprint:typer -e」フラグを使用します。
# scala -Xprint:typer -e "for (i <- 0 to 100) yield i"
マクロはどうですか?
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
これは、リクエストに応じて REPL で直接使用できます。
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])
他の任意の式でも機能します。
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))
}
これはおそらく、任意の時点でデータをコンパイルまたはファイルにダンプすることなく、求めているものに最も近いものです。マクロは REPL で直接定義するか、:load
コマンドでロードされた外部ファイルで定義できます。
単純な脱糖後の結果を確認するには、-Xprint:parser
オプションを使用します。
という名前のこの単純な入力ファイルがある場合test.scala
:
object Test {
for(x <- List(1,2,3); y <- List(4,5,6)) yield x*y
}
scalac -Xprint:parser
次に、出力を使用してコンパイルします。
$ 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)))))
}
}
これを行うために適用可能なコンパイラ フェーズの完全なリストを取得するには、次のように-Xprint:<phase>
します。
$ 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
この-Xprint:<phase>
オプションは REPL にも適用できるscala
ため、REPL にも適用できます。ただし、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>
scala 2.11 では、 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)