3.28.2015

Scala: Getting Started with Dotty

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 を試す。

$ ./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 で実行できず。

$ 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 トレイトを使うのを避けたら、とりあえず動くようになった。

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!

新機能を試す

プロシージャ構文の廃止

関数定義の = を省略すると、

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リテラルはコンパイルエラーになる。

package hello

object xml_ng {
  def main(args: Array[String]): Unit = {
    println(XML Literal)
  }
}
$ dotc ./src/03_xml_literals_ng.scala
./src/03_xml_literals_ng.scala:5: error: xml is not a member of scala.type
    println(XML Literal)
             ^
one error found

将来は xml"" で中置記法に対応するとのことだが、現時点ではまだ使えない模様。

package hello

object xml_literals {
  def main(args: Array[String]): Unit = {
    println(xml"""XML Literal""")
  }
}
$ dotc ./src/04_xml_literals.scala
./src/04_xml_literals.scala:5: error: xml is not a member of StringContext
    println(xml"""XML Literal""")
               ^
one error found
トレイト・パラメータ

トレイトがパラメータを取れるようになる。

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
今回使用したコード

0 件のコメント:

コメントを投稿