Scala をちょっといじってみたメモ

Java: Evolutionary Dead End』にインスパイアされて、以前からあちこちで話題の Scala をちょっといじって見た。最近ハヤりの Java VM 上で動く言語だけど、関数型っぽいトコもあるとかって聞いてて気にはなってたですよ。そう言えばオブラブの冬イベントでもちょろっと名前が出てましたね。

早速パッケージを落として展開。っつーか MacPorts にもパッケージは用意されてるんだけど、そもそも MacPorts には色々とムカつかされることが多いので tar.bz2 からの野良インストール。CygwinLinux でなら必ずパッケージを作っていたのに何なんだこの差は。

で中をごそごそと探検してみると…share/scala/misc/scala-tool-support/emacs の下に Emacs 用のモードを発見。っつーか vim 用の *.vim もあるし enscript の *.st、挙げ句に XCode 用の spec までもが入ってる。何この極端な充実っぷり。Eclipse プラグインまであるみたいですけどそんなの関係ねぇ。

scala-mode を使ってみる

scala-mode のバイトコンパイルは make 一発。site-lisp に突っ込んで、.emacs

(require 'scala-mode-auto)
(setq scala-interpreter "/opt/java/scala/bin/scala")

と書くだけですよ。これで拡張子 .scala のファイルを開くと、scala-mode になります。

あと、変数 scala-interpreter には利用する scala コマンド(Scalaインタプリタ)を指定。デフォルトは単に「scala」なので、scala にパスが通っていれば設定の必要ナシ。

scala-mode の主なキーバインドはこんな感じ。

C-c C-b scala-eval-buffer
C-c C-c comment-region
C-c C-l scala-load-file
C-c C-r scala-eval-region

で、Scala ソースファイルの編集中に「run-scala」すれば、変数 scala-interpreter に設定された scala が対話的に立ち上がるのはお約束。っつーかこれやらないと C-c C-b とか C-c C-r とか意味ありませんから。

まぁ、味見中の今日のところは Emacs 内部で eval だけで幸せなんだけど、ちょっと大きくなってきたらビルドツールが必要そうだなぁ。make 単体でもイケる気はするんだけど、何か特化したツールもあるんだろうか。調べろ俺。

チュートリアルを読んでみる

share/scala/doc/scala-documentation に「ScalaTutorial.pdf」があるので、取り敢えず一通り読んでみた。15 ページ程度だしやさしく書いてあるので、Java プログラマなら一通り読みこなせるんじゃないかと思います。写経度もそれなりなので、C-c C-r しながら読むと良さそう。

でも今日はチュートリアルを読んだだけで終わってしまったけど。

その他

ふと気がついたコトを並べてみる。

scala.dbc

Scala には Javadoc に相当する「Scaladoc」なるものがある。API 仕様もバッチリだよママン。

というわけで API 仕様をつらつらと眺めてみると、scala.dbc なるパッケージを発見。これ以下にはデータベース接続用のアレコレが用意されているみたい。でも scala.dbc.vendor を見つけておっと思ったら、PostgreSQL しか定義されてなかった。しょんぼり。

演算子もメソッドだよ

「Everything is an object」が売りだけあって、数値も関数もオブジェクト。しかも、数値用の演算子もメソッドみたい。チュートリアルには「1 + 2 * 3 / x」とは「1.+(2.*(3./(x)))」のことだ! みたいな例が載っててちょっと嬉しい。

と思ったら、どうも結果が妙。

3/(5) → Int = 0

なのに、

3./(5) → Int = 0.6

となる。何故だろう。どこかで Integer → Double 変換が入ってるなぁ…。

というわけで、ちょっと Jad ってみることに。Scala のソースは scalac というコンパイラで .class ファイルになるので、Jad るのは簡単。まず

val x = 3
val y = 5
println(3/(5))
println(3./(5))
println(x/(y))
println(x./(y))
println(3/(y))
println(3./(y))
println((3)./(y))

なんて簡単なソースを用意して、scalac して Jad。結果はと言うと…

Predef$.MODULE$.println(BoxesRunTime.boxToInteger(0));
Predef$.MODULE$.println(BoxesRunTime.boxToDouble(0.59999999999999998D));
Predef$.MODULE$.println(BoxesRunTime.boxToInteger(x / y));
Predef$.MODULE$.println(BoxesRunTime.boxToInteger(x / y));
Predef$.MODULE$.println(BoxesRunTime.boxToInteger(3 / y));
Predef$.MODULE$.println(BoxesRunTime.boxToDouble(3D / (double)y));
Predef$.MODULE$.println(BoxesRunTime.boxToInteger(3 / y));

ちょっとびっくりしたのは、scalac の段階でリテラルの演算は完了しているトコ。でも本題は Integer → Double 変換。よく見ると、3 と 3D が入り混じってる。

どうやら、「3.」における「.」はオブジェクト「3」に対するメソッド呼び出しの表記として解釈される前に、浮動小数リテラル「3.」(= 3.0)として解釈されるみたい。これを防ぐには、「(3)」のように括弧で括れば OK。

FAQ より

勢い余って FAQ も読んでみましたよ。

ループ
var i = 1; while (i <= 1000000) { ...; i += 1 }

に加えて

for (i <- 1 to 1000000) { ... }

が使える。下のほうがカッコいいけど現時点では上のほうが効率的みたい。

例外

Java の検査例外は当然存在するんだけど、Scala そのものには検査例外に対応する考え方はないみたい。「理論上は」素晴らしいけど、実際はそうじゃないってのが理由だそうな。

try 〜 catch はこんな感じ。

try {
  // ...
} catch {
  case ioe: IOException => ...
  case e: Exception => ...
}

パターンマッチが使えるのが嬉しい。例外処理がすっきりしそう。

break / continue

brake と continue はないので、他の方法で実装しろとな。言語を複雑にするので、将来も用意する予定はないとのこと。

Java との相互運用

Scala からは Java のクラスはフツーに使える。でも Scala には当然ながら Java にはない考え方が多いので、Scala のクラスを Java から使うのは(不可能ではないにしろ)ちょっと難しそう。