Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

ScalaのApplication traitの是非

ScalaにApplicationというtraitがある。
これを継承すると、mainメソッドを記述する必要がなくなり、そのクラスのbody部分が実行される。

object ApplicationTrait extends Application {

  Console println("Hello Scala!")
  
}

実行結果。

Hello Scala!

これはApplication traitがmainメソッドを定義しているから。

trait Application {
  /** The time when execution of this program started.
   */
  val executionStart: Long = currentTime

  /** The default main method.
   *
   *  @param args the arguments passed to the main method
   */
  def main(args: Array[String]) = {
    if (getProperty("scala.time") ne null) {
      val total = currentTime - executionStart
      Console.println("[total " + total + "ms]")
    }
  }
}

おー便利!とか思ったけど、Scala Blogで反論が出てた。

It's a bad idea for two reasons.

First, concurrency is broken. The JVM does not allow other threads to access a class during initialization. You can easily get yourself into deadlock by writing concurrent code in classes that use Application. (See Ticket #746)

Second, performance is broken. HotSpot doesn't bother to optimize class initialization. This can cause performance differences of several orders of magnitude in classes that use Application. (See this blog post and the ensuing mailing list discussion.)

While the Application trait may save you a line of code, it comes with many severe and hidden pitfalls. Hopefully it will be removed from the standard library, or at least deprecated. In the meantime, save yourself the headache and avoid Application in your applications.

Scala Blog

これが悪いアイデアである理由は2つある。


1つは並行性を壊してしまうことだ。
JVMはクラスを初期化する間他のスレッドにアクセスすることを許可していない。
Applicationを使うクラスで並行実行するコードを書けば簡単にデッドロックに陥る(チケット#746を参照のこと)。


2つめはパフォーマンスを壊してしまうことだ。
ホットスポットはクラスの初期化をわざわざ最適化しない。
これはApplicationを使うクラスで桁違いのパフォーマンスの違いを引き起こすかもしれない(このプログへのポストを参照しメーリングリストの議論を確かめること)。


Application traitは何行かのコードを節約するが、多くのシビアで隠れた問題を引き起こす。
願わくばスタンダードライブラリから削除するか、少なくともdeprecatedにしてほしい。
それまではアプリケーションでApplicationを使わないようにしよう。

そもそも何でbody部に書いた処理が実行される?

おまけ。Scalaではbody部に書いた式は、コンストラクタを意味する。
Application traitによってmainメソッドが定義されたわけだから、
プログラムを実行するとmainメソッドを実行するために、インスタンスを生成する。
# Scalaにstaticの概念はない。
body部はコンストラクタのコードだから、生成した際に実行されるというわけ。
一種のトリックコードだって言ってるね。

In Scala, the body of a class also doubles as its primary constructor. Likewise, the body of a singleton object doubles as its only constructor. The Application trait works by running the entirety of the application in the constructor for MyScalaApp. This is a clever trick for reducing a bit of boilerplate code, but it turns out to be a catastrophically bad idea.

Scala Blog