Scala2.11.7からのJava8に対応した新しいコンパイルオプションを試してみた

"新しい" とは、今まで全部無名classにコンパイルしてたラムダを、invoke dynamic使って動的に生成するようになるとかそういうやつ。


ラムダ以外の最適化もやってるようですが?詳しくは知りません。
Scala 2.12から正式に入るらしいですが、(これ書いてる時点から見て)つい昨日くらい(2015/06/25)に出たScala2.11.7でも、明示的にオプションを指定すれば、実験的オプションとしてですが、使えるそうです。

内部の仕組みは、以下のScaladays2015のスライド見るとか、あとは各自ググってください

公式に書いてありますが、2.11.7においてJava8のバックエンドを最大限(?)に有効活用する方法は

https://github.com/scala/make-release-notes/blob/0d392b1aa/experimental-backend.md#emitting-java-8-style-lambdas

  • (当たり前だが)Javaの8をインストー
  • Scala2.11.7以上に設定する
  • いくつかコンパイルオプションを指定
  • libraryDependenciesに専用の依存指定

で、つまりsbtのbuild.sbtで言うと以下のような感じ

libraryDependencies += "org.scala-lang.modules" %% "scala-java8-compat" % "0.5.0"

scalacOptions ++= List("-Ybackend:GenBCode", "-Ydelambdafy:method", "-target:jvm-1.8")

scalaVersion := "2.11.7"

コンパイルオプション追加だけではあまり意味なくて(それでも新しいバックエンド使われて一部最適化されるらしいが)、2.11.7時点ではscala-java8-compatの依存を明示的に追加しないとラムダがinvoke dynamicになってjarサイズ削減の効果が得られないようです、注意しましょう。
コンパイラ側が
コンパイルオプションが指定されていて、かつscala-java8-compatがpathにある場合」
という条件で最適化するようです
(つまりscala-java8-compatが存在するかどうかでコンパイラ自体の動作が全然異なるという、少し気持ち悪い?挙動)


さて、それでjarサイズが大きいことで有名な(?) Scalazをこれでコンパイルしてみました。使ったのは、これ書いてる現在の最新のtreeです。

結果、

という結果でした。

36%ほど削減されて、オプション指定なしのときと比べて64%くらいのサイズになりましたね。

3分の1くらいまで減るかな―と、長年期待していたのですが、思ったほどは減らなくて少しがっかりしています・・・(´・ω・`)


これ、まだ最適化の余地あって、2.12.0のfinalが出る頃には3分の1くらいになったりしないんですかね・・・。

まぁそれにしても、36%削減は、他の方法では実現できないのでありがたいです。


さて
「jarのサイズ小さくなるから、scalaz 7.2.x の Scala 2.11 は Java7サポートやめて、このオプション指定するようにしようぜっ!」
と、提案することを以前から考えていたんですが、なんとScala2.11.7ではsbtがこのオプションつけると色々正しく動かないようです!???
追記: Scala2.11.8では直ったようです

https://github.com/sbt/sbt/issues/2076

cleanでも直らない(targetディレクトリ丸ごと削除とか再起動必要?)、というかなり酷い感じで実用的じゃなさそうなので、少なくともsbtがなおるまでは、scalazへのその提案はできないですね・・・。



他にも試したので追記