バイオインフォマティクスの分野では、現場で動くコードを書けるプログラミング言語でないと使えません。 Scalaが本当に現場で使えるかどうかわかるまで、実際にコードを書いてみたり、開発環境、ライブラリを調べるなど調査に費やした時間も膨大でした。 そのときの自分に教えてあげるつもりでScalaの利点をここにまとめていきます。
Scalaでは関数型言語のスタイルに固執する必要がない。命令型、副作用のあるコードも書けるので、慣れるに従い関数型のスタイルに近づければ良い。 C++, Java、Perlなど命令型のコードが多い言語に慣れていると、最初のうちは関数型のコードをどう書けば良いかわからないことが多いと思うが、 Scala APIにあるライブラリの使い方に習熟してくると、これらをどう組み合わせてコードを書けばよいかが見えてくるようになる。
Martin OderskyのProgramming in Scalaの本にも、well-trained eyes (訓練された目では) という表現がよく出てくるが、 私自身も、Scalaを覚えたての頃と、Scalaに慣れた現在ではコードを見る目、書き方がずいぶんと変わった。
型推論や構文の工夫により、Rubyなど動的型付け言語と同じくらいコーディングがしやすくなっている。
// Mapの作成
val m = Map(1 -> "A", 2 -> "B", 3 -> "C")
for((key, value) <- m) {
...
}
これを敢えて冗長に書くと、、、
val m : Map[Int, String] =
Map.apply[Int, String](Seq[Tuple2[Int, String]]
(new Tuple2[Int, String](1, "A"), new Tuple2[Int, String](2, "B"), new Tuple2[Int, String](3, "C"))
// 型名をすべて補って書き下すと...
m.foreach[Tuple2[Int, String]]{ (entry:Tuple2[Int, String]) =>
entry match {
case (key:Int, value:String) => ...
}
}
となるが、このような詳細をコーディング時に気にする必要がない。(もちろん詳細を知っておくと、Scalaでコードライブラリを開発するときの力になる)
IntelliJ, sbtなどが コミュニティでよく使われており、大きなプロジェクトの開発にもScalaは実用的に使えるようになってきた。
EclipseでScala IDE for Eclipseを使っても一応開発できるが、2012年7月の時点では、 IntelliJ + Scalaプラグインの方が使い勝手(syntax highlight, type inferenceによる文法エラーの検知)が良い。
sbtで特筆すべき点は、コードの更新をモニターして、変更があればすぐ再コンパイルを行い、テストコードの実行までを自動で行ってくれる。 Scalaのコードはコンパイルに多少時間がかかるが、この機能により開発時のストレスが少ない。
Javaで一般的なMavenによる開発スタイルを踏襲することもできるが、sbtを使う方が良い。 mavenにできてsbtにできないこともたくさんあるが、sbtにできてmavenにできないことを実装する方が大変に思う。 sbtの拡張はsbtのソースコードとにらめっこして、Scalaで書けばよい。作成したプラグインもGitHubに置くなどの手段が使える。一方mavenプラグインでは、XMLによる仕様の記述、クラスの階層関係の把握、maven centralにdeployするなど、一筋縄ではいかない箇所が多くある。
Javaのコードとの親和性に関しても、Scala 2.8でcollectionクラスの大幅な改善により、格段に使い勝手が良くなった。 Scala2.9では、並列処理のためのコレクションの拡張が行われており、マルチコアのための計算も簡単になっている。 実際、私自身も10CPU以上を使った演算などを日常的に行えるようになって助かっている。
ScalaはJavaと同様JVMの上で動く言語であるが、オブジェクト指向言語としてだけ見ても、Javaと比較して改善されている点が多々ある。
例えば、Stringにはformatというメソッドはないが、StringOpsなどに自動的に変換して機能を追加できる。
"Hello %s!".format("World")
// Javaでは以下のように書かなければならなかった
String.format("Hello %s", "World")
Covarianceの例 (List[Banana], List[Apple]はList[Fruit]のsubtypeとして扱える)
trait Fruit
class Apple extends Fruit
class Banana extends Fruit
// ListはList[+A] (covariance)として定義されている
val l : List[Fruit] = List[Banana](new Banana, new Banana) ++ List[Apple](new Apple)
Function2[-A, +B]
など。method(..) throws xxException
という形でexceptionの型までメソッドに指定しなくてはならないために、汎用的なライブラリを書く障害になっていた。例えば同じようなコードを再利用できる場所でも、DBException, IOExceptionなど内部で発生する例外の型が違うために、APIでは親クラスのthrows Exceptionを使うように設計しなくてはならず、APIを使う側では何のエラーだかわからないExceptionをcatchするコードを大量に書く必要があった。Scalaでは、throws … と書かなくても良くなり、programのmain関数内など、必要最低限の位置で例外をcatchすれば良いようになっている。Scalaを作ったMartin Odersky氏への以下のインタビュー記事を読むと、なぜScalaの言語が今のようなデザインになっているのかがよくわかる。妥協もあり、積極的に関数型言語、オブジェクト指向言語の融合をはかった部分もあり。Javaの不便を乗り越えるとともに関数型言語の良い面を取り入れるため最大限の努力をしている様子が伺える。