Scala: dotty コンパイラを試す
Scala Days San Francisco のキーノートでも触れられた、Scala の新コンパイラ dotty を試す。
環境
- OS X Yosemite: Version 10.10.2
- Processor: 2.6 GHz Intel Core i7
- Memory 16GB 1600 MHz DDR3
- 以下インストール済み
- Oracle JDK 8 u40 (JDK 7 ではビルドできなかった)
- git: version 1.8.4.3
- sbt: version 0.13.0
インストール
まずは、作業ディレクトリに scala/scala および dotty のソースコードをダウンロード。
$ cd <YOUR_WORK_DIR> $ git clone git@github.com:scala/scala.git $ git clone git@github.com:lampepfl/dotty.git |
Java コンパイラのバージョンを確認 (1.8.x であればOK)
$ javac -version javac 1.8.0_40 |
JDK 8 をインストールしているのにバージョンが古い場合は、環境変数などをチェック
$ export JAVA_HOME=`/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/java_home -v "1.8"`
$ echo $JAVA_HOME
/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home
$ PATH=${JAVA_HOME}/bin:${PATH}
$ javac -version
javac 1.8.0_40
dotty をビルド
$ cd dotty $ sbt compile |
100個ほど warn メッセージが表示されたが、Travis CI でも出ているので無視。所要時間は 58秒ほどだった。
sbt run を実行すると、usage が表示された。
$ set run |
Usage: scalac <options> <source files> where possible standard options include: -Dproperty=value Pass -Dproperty=value directly to the runtime system. -J Pass directly to the runtime system. -bootclasspath Override location of bootstrap class files. -classpath Specify where to find user class files. -d destination for generated classfiles. -dependencyfile Set dependency tracking file. -deprecation Emit warning and location for usages of deprecated APIs. -encoding Specify character encoding used by source files. -explaintypes Explain type errors in more detail. -extdirs Override location of installed extensions. -feature Emit warning and location for usages of features that should be imported explicitly. -g Set level of generated debugging info. -help Print a synopsis of standard options -javabootclasspath Override java boot classpath. -javaextdirs Override java extdirs classpath. -language Enable one or more language features. -no-specialization Ignore @specialize annotations. -nobootcp Do not use the boot classpath for the scala jars. -nowarn Generate no warnings. -optimise Generates faster bytecode by applying optimisations to the program -pagewidth Set page width -print Print program with Scala-specific features removed. -sourcepath Specify location(s) of source files. -strict Use strict type rules, which means some formerly legal code does not typecheck anymore. -target Target platform for object files. All JVM 1.5 targets are deprecated. -toolcp Add to the runner classpath. -unchecked Enable additional warnings where generated code depends on assumptions. -uniqid Uniquely tag all identifiers in debugging output. -usejavacp Utilize the java.class.path in classpath resolution. -verbose Output messages about what the compiler is doing. -version Print product version and exit. @<file> A text file containing compiler arguments (options and source files) |
UT を実行
$ sbt test ...snip... [info] Passed: Total 106, Failed 0, Errors 0, Passed 106 |
無事、全てのテストが通った。所要時間は 157秒。
REPL
Scala REPL で dotty の AST を試す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | $ ./bin/dotc -repl Welcome to Scala version 2.11.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_40). Type in expressions to have them evaluated. Type :help for more information. scala> import test.DottyTypeStealer._; import dotty.tools.dotc.core.Types._; import test.DottyTypeStealer._ import dotty.tools.dotc.core.Types._ scala> val s = stealType("object o { type X }", "o.X") s: (dotty.tools.dotc.core.Contexts.Context, List[dotty.tools.dotc.core.Types.Type]) = (Context( owner = module class , scope = Scopes() owner = module class , scope = Scopes() owner = module class , scope = Scopes() owner = module class , scope = Scopes() owner = module class , scope = Scopes() owner = module class , scope = Scopes() owner = val , scope = Scopes(module _root_) owner = val , scope = null owner = val , scope = null,List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,)),o),X))) scala> implicit val ctx = s._1 ctx: dotty.tools.dotc.core.Contexts.Context = Context( owner = module class , scope = Scopes() owner = module class , scope = Scopes() owner = module class , scope = Scopes() owner = module class , scope = Scopes() owner = module class , scope = Scopes() owner = module class , scope = Scopes() owner = val , scope = Scopes(module _root_) owner = val , scope = null owner = val , scope = null scala> val t = s._2(0) t: dotty.tools.dotc.core.Types.Type = TypeRef(TermRef(ThisType(TypeRef(NoPrefix,)),o),X) scala> val u = t.asInstanceOf[TypeRef].underlying u: dotty.tools.dotc.core.Types.Type = TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix,scala)),Nothing), TypeRef(ThisType(TypeRef(NoPrefix,scala)),Any)) scala> t.show res0: String = o.X scala> u.show res1: String = "" |
ただし、ここで動いているのはあくまでも Scala REPL であるため、dotty 固有の構文は受け付けられない。
Hello world
まずは、付属のサンプルコードを試してみたが、 ClassFormatError で実行できず。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | $ cat ./examples/hello.scala package hello object world extends App { println("hello dotty!") } $ ./bin/dotc ./examples/hello.scala warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$GenMapFactory$$CC; assuming they are invariant warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$MapFactory$$CC; assuming they are invariant warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$GenSetFactory$$CC; assuming they are invariant warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$SetFactory$$CC; assuming they are invariant four warnings found $ ll hello total 16 -rw-r--r-- 1 user staff 1134 3 28 18:41 world$.class -rw-r--r-- 1 user staff 400 3 28 18:41 world.class $ scala hello.world java.lang.ClassFormatError: Illegal method name "initial$scala.DelayedInit" in class hello/world$ at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:760) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) at java.net.URLClassLoader.access$100(URLClassLoader.java:73) at java.net.URLClassLoader$1.run(URLClassLoader.java:368) at java.net.URLClassLoader$1.run(URLClassLoader.java:362) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:361) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at hello.world.main(hello.scala) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at scala.reflect.internal.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:70) at scala.reflect.internal.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31) at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:101) at scala.reflect.internal.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:70) at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101) at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:22) at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39) at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:29) at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39) at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:65) at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:87) at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:98) at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103) at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala) |
App トレイトを使うのを避けたら、とりあえず動くようになった。
1 2 3 4 5 6 7 | package hello object world { def main(args : Array[String]) : Unit = { println( "hello dotty!" ) } } |
$ dotc ./src/01_hello_world.scala warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$GenMapFactory$$CC; assuming they are invariant warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$MapFactory$$CC; assuming they are invariant warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$GenSetFactory$$CC; assuming they are invariant warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$SetFactory$$CC; assuming they are invariant four warnings found $ scala hello.world hello dotty! |
新機能を試す
プロシージャ構文の廃止
関数定義の = を省略すると、
1 2 3 4 5 6 7 | package hello object world _ ng { def main(args : Array[String]) { println( "hello dotty!" ) } } |
コンパイルエラーになる。
$ dotc ./src/02_hello_world_ng.scala ./src/02_hello_world_ng.scala:4: error: '=' expected but '{' found. def main(args: Array[String]) { ^ one error found |
XMLリテラル
旧形式のXMLリテラルはコンパイルエラーになる。
1 2 3 4 5 6 7 | package hello object xml _ ng { def main(args : Array[String]) : Unit = { println( < a > XML Literal < /a > ) } } |
$ dotc ./src/03_xml_literals_ng.scala ./src/03_xml_literals_ng.scala:5: error: xml is not a member of scala.type println(<a>XML Literal</a>) ^ one error found |
将来は xml"" で中置記法に対応するとのことだが、現時点ではまだ使えない模様。
1 2 3 4 5 6 7 | package hello object xml _ literals { def main(args : Array[String]) : Unit = { println(xml "" "<a>XML Literal</a>" "" ) } } |
$ dotc ./src/04_xml_literals.scala ./src/04_xml_literals.scala:5: error: xml is not a member of StringContext println(xml"""<a>XML Literal</a>""") ^ one error found |
トレイト・パラメータ
トレイトがパラメータを取れるようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 | package hello object trait _ parameter { trait CoordLike(x : Double, y : Double) { def show() = println(s "($x, $y)" ) } object Coord extends CoordLike( 1.0 , 2.0 ) def main(args : Array[String]) : Unit = { Coord.show() } } |
コンパイルはできたが、残念ながら実行はできなかった。
$ dotc ./src/05_trait_parameter.scala warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$GenMapFactory$$CC; assuming they are invariant warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$MapFactory$$CC; assuming they are invariant warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$GenSetFactory$$CC; assuming they are invariant warning: encountered F-bounded higher-kinded type parameters for type scala$collection$generic$SetFactory$$CC; assuming they are invariant four warnings found $ scala hello.trait_parameter java.lang.ClassFormatError: Illegal method name "initial$hello.trait_parameter$$CoordLike" in class hello/trait_parameter$$CoordLike |
パッケージの指定(package hello)をなくしたら、今度は別のエラーが出た。
$ scala trait_parameter java.lang.ClassFormatError: Method x in class trait_parameter$$CoordLike has illegal modifiers: 0x402 |