CNetでも報道された通り、Sunが独自のスクリプト言語JavaFX Scriptを発表した。テクノロジーの優劣だけで決まるものではないので、この試みがうまく行くかどうかは何とも予測しがたいが、とりあえず言語仕様が公開されたので目を通してみた。
私なりに興味深いと思った点は以下の5つ(ただし、私なりの拡大解釈が多少入っている可能性もあるので要注意)。
1.宣言型のUIをサポートしていること
宣言型大好き人間の私としては、この方向性は大賛成(ちなみに、UJMLも宣言型のUI言語^^)。"押してね!"というラベルがついたボタンを表示するには、こう書けば良い。
Frame {
content: Button {
text: "押してね!"
action: operation() {
System.out.println("押してくれて、ありがとう");
}
}
visible: true
}
2.配列・文字列のオペレーション
C++やJavaが進歩を止めている間に、スクリプト言語は大幅に進歩してきたが、やはり生産性の向上という意味では、配列と文字列のオペレーションがライブラリーではなくて言語レベルでキチンとサポートされている点がスクリプト言語の一番の強みと私は感じている。もちろんSunもそこははずさずに来た。下のサンプルは、配列に対するinsertやdeleteなどのオペレーション。
var x = [1,2,3];
insert 10 into x; // yields [1,2,3,10]
insert 12 before x[1]; // yields [1,12,2,3,10]
delete x[. == 12]; // yields [1,2,3,10]
delete x[. >= 3]; // yields [1,2]
insert 5 after x[. == 1]; // yields [1,5,2];
insert 13 as first into x; // yields [13, 1, 5, 2];
delete x; // yields []
その中でも便利そうなのが、SQLのように配列に対してqueryをかけて別の配列を作るselect。下のサンプルは、各アルバムの曲の中から、アルバムのタイトルと一致するトラックのインデックスを新たな配列として作っている。
var titleTracks =
select indexof track + 1 from album in albums,
track in album.tracks
where track == album.title;
このqueryはforeach文にも適用できて、その場合は、
foreach (album in albums,
track in album.tracks
where track == album.title)
となる。
そして文字列のオペレーション。スクリプトを使いこなしている人たちにはあたり前だが、文字列中の変数やステートメントを展開してくれるのはやはり便利だ。
var name = 'Joe';
var s = "Hello {name}"; // s = 'Hello Joe'
var answer = true;
var s = "The answer is {if answer then "Yes" else "No"}"; // s = 'The answer is Yes'
3.非同期な実行
興味深いのが、do later ステートメントを使って、指定したブロックのコードを非同期で実行できること(ただし、新しいスレッドを作るわけではなく、単にキューに入れて後から実行する)。便利な機能ではあるが、サイド・エフェクトのことを考えずに間違った使い方をするプログラマーが続出しそうで、少し心配。
import java.lang.System;
var saying1 = "Hello World!";
var saying2 = "Goodbye Cruel World!";
do later {
System.out.println(saying1);
}
System.out.println(saying2);
逆に単なるdoステートメントの方が、別スレッドを作ってdo内部のコードを実行させるように出来ている点が興味深いが、スレッドのディスパッチに関しての記述にまだあいまいさがあり、このあたりの言語仕様はまだ十分に練られていないように感じた(今後変更する可能性あり?)。
4.Update Triggerという考え方
これはなかなか面白い。「新しいオブジェクトが作られた」とか、「オブジェクトの変数が変更された」などのイベントをキャッチして、それに対応した処理を記述できるのだ。そのためコンストラクターは不要で、代わりにこんなプログラムを書くことになる。
class X {
attribute nums: Number*;
}
trigger on new X {
insert [3,4] into this.nums;
}
var x = new X();
System.out.println(x.nums == [3,4]); // prints true
5.Incremental and Lazy Evaluation
Haskellを知っている人には珍しくもないコンセプトだが、C++/Javaプログラマーに一口で説明するには少し難しいのがこれ。ある変数に実際の値を代入するのではなく、計算式を代入しておく機能がIncremental Evaluationだ。そうしておくと、エクセルのスプレッドシートのフォーミュラのように、参照先の変数の値が変化すると自動的に新しい値を計算し直してくれるのだ。さらに、それをlazyと宣言しておくと、その計算を実際に計算結果を参照する時まで遅らせておいてくれるのが、Lazy Evaluationである。
Sunが提供しているサンプルは少し分かりにくいので、先の宣言型のUIを例として、このIncremental Evaluationを説明するとこうなる(注意:ここが私なりに拡大解釈してしまっている可能性がある部分)。
label = "押してね!";
message = "押してくれて、ありがとう";
Frame {
content: Button {
text: bind label;
action: operation() {
System.out.println(message);
}
}
visible: true
}
とすると、上の例と同じく"押してね!"というラベルのボタンが表示されるのだが、その後、
label = "押さないで!";
message = "押さないでって言ったのに!";
と変数labelの値を変更するだけで、ボタンのラベルが自動的にアップデートされるのだ(再度UIを宣言し直す必要はない)。これにより、HTMLのDOMに相当するものが不要になる点が注目だ。
まとめ
全体として見ると、コンパクトにまとまった良く出来たスクリプト言語というのが私の感想。宣言型UIは大歓迎だし、それとIncremental Evaluationの組み合わせには大きな可能性を感じる。ただし、Incremental Evaluationを実現するには、変数間の依存関係をグラフとして持っておく必要があり、そこをどのくらい効率良く実装できるかが勝負だろう。
一つだけどうしても好きになれないのが、doステートメントが別スレッドを作ってしまう点。I/O用のバックグラウンド・スレッドの存在はプログラマーからはあえて隠し、その代わりにI/O関係のAPIをすべて非同期にした方がずっとすっきりとした言語仕様になると思う(長年UIを作る仕事をしてきたが、マルチスレッド・プログラミングとUIはものすごく相性が悪いのだ)。