02:19 06/04/28

砦

大学であった Guy Steele 氏の講演を聴いてきました。お題は、Sun で今研究開発中の "Fortress" という新しい言語に ついて。キャッチフレーズは "To Do for Fortran What Java Did for C" だそうで、 基本は、大規模な科学技術計算・並列計算のための（つまり、Fortranがこれまで担ってきた 作業をするための）言語です。ただ、それを実現する枠組みはもっとgeneralな何かになって いるよ、と。

並列計算特有の話はほとんどなくて、「言語設計のやり方」 的な内容をお話しされていて、 まさにそれが聴きたかったのでとても面白かったです。以下雑メモ。

この問題提起に対する答えは当然

コンパイラに組み込む機能は最小限にとどめる

できる限りライブラリによって何もかもを実現するべし

実際のアプリケーションを眺めてみて「言語にこんな機能があったらここはもっと 楽に書けるなあ」と思ったら、そんな機能をコンパイラに実装する……という方法は もちろんとらない。そんな機能を実現するライブラリを書く。コンパイラ/言語実装者の 視点で見るときであっても、「そんな機能を実現するライブラリを書きやすくする ような言語の拡張はなんだろう？」という視点で考える。

ライブラリでやる、というアプローチに対する批判も当然ご想像の通りのがあるわけで

それはちゃんと効率の良いプログラムになるのか？

最適化の技術は日々進歩している。特に、Java VMが成し遂げた実行時JIT最適化 のような分野。ライブラリによる、クラスや関数やメソッドを使った動的な実装を、 コンパイラ組み込みの知識によって静的に展開されるような実装と同等の パフォーマンスにまで持って行く最適化は"実現できる"、というスタンス。

プロジェクト的に一つの年限が2010年らしく、まあ2010年を待っててくれよ的スタンス。

私(k.inaba)は、この徹底してライブラリでやるというスタイルがとても好きなのですが、 同時にそのスタイルを批判している言語をメインで使っている人でもあって、なかなか考えさせられます。 リンク先の批判の 「5: 結局"標準"ライブラリとなってしまえばエンドユーザにとって 柔軟性は何も変わらない」 については同じ懸案事項ではあるようで、「Fortressで はMonolithicな標準ライブラリは持たずに、常にライブラリの各部分をいろんな実装に 取っかえひっかえすることをむしろ推奨すらする」いう方針を明確にしていました。 「1,3,4,6 ... 効率や使い勝手を真に組み込みの機能に並べることは難しい」という点は、 「それを実現するのだ」というのがまさにFortressのチャレンジングなところであって、 「それが実現できる」という言葉に期待ですね。「2: ライブラリで実装した機能を間違って 使ったときに、コンパイラ組み込みの実装並のわかりやすいエラーメッセージが出せますか？」 というのが一番面白い問いだと思うけれど、どうなんだろう。閑話休題。

他に、ライブラリで実現された機能って言語組み込みの機能より読みにくいし 使いにくいよね、的な話もありまして。

どこのJavaのどこのBigDecimalの話かは言いませんが、かけ算するのに x.multiply(y) って、もうやってられませんよね。

って、もうやってられませんよね。 そこで演算子オーバーロード。

ちなみに Fortress ではかけ算は x*y と書く必要すらない。 x y です。

Generalized Overloading ｷﾀｺﾚ!! …っていうか、並置の意味の オーバーロードはマジメな話本気でやりたいのでこれは個人的には嬉しい方向。

C++の演算子オーバーロードはダメダメだった。何故か。

使える演算子の数が、定義済みの10数個しかなかったからだ。なので、その分野に とって自然な演算子は使えず、C++に既にある別の演算子で代用しないといけなかった。 結果として、記号を見ても何が行われるかわからない多重定義された演算子が溢れる。

しかし今や時代はUnicodeである。 ASCIIに縛られなければ、自然な記号は 次々手に入る。括弧なんか29種類もありますよ。

特に数値計算で出てくるような記号… α,ρ,π のようなギリシャ文字はある。normを 意味するのに便利そうな ∥x∥ みたいな記号もある。総和を取る演算子に Σ。 集合演算∪∩∈なども一通り。などなどなど。

そもそもかけ算も * じゃなくて ・ や × もあるし。

もちろん記号が溢れているからって乱用するのはダメ。ちゃんと、その記号が その分野で普通に使われているものなら導入し、そうでないなら導入しない、という ことには注意しないといけない。

できるだけスタンダードな数式に近い表現を可能とする方向へ。

「スタンダードな数式っぽい書き方というと単に記号の問題だけでなくて、例えば 0 < x < 1 みたいな表記の問題もあると思うけど、そういうのは どうするんでしょう？」 って質問したら 「その書き方は既にFortressでサポートされてます」 って返されました。うへ。そういうのは演算子オーバーロードの枠組みは越えるけれど、 数式の書き方としてよく使われているものだし、これは個別にサポート、とのこと。 他に、内包表記 {f(x,y) | x←S, y←A, x≠y} なんかも、それが数式としてスタンダードな 表記なのでこれはこれで対応。A^2と書くと行列Aの2乗だけれど、A^Tと書くと行列Aの転置に なるようになっていたりする、と。（最後の例は識別子Tに特別な意味を持たせれば普通に 演算子オーバーロードの範囲内だと思うけど、そういうのは嫌ったのかな…？）

他の人からの質問で、「我々にはこの10本の指しかないわけですが(^^;、それでASCIIの範囲で プログラムは書けてもUnicodeをフルに使ってプログラム組むのは、それは本当に生産性 あがるのでしょうか？」 というのもありました。「一度その記号を打ったらあとはコピペ」 で割と困らないよ、というのが回答の一つ、あとはスタンダードに、「代替記法」を用意。 α でも alpha でも同じ意味になるようにする。各種括弧については、ASCIIで書けるような 記法を用意しておく、etc。打ち込むのはASCIIな方で、開発環境がpretty printしてくれる とか…Wikiっぽいイメージ？とかそんなこんな。

プログラミング言語ではないけど、MathML がUnicodeで記号を入れることを許す 言語なんですが、自分の経験ではあれは割とややこしい気がしていました。 ギリシャ文字のΣと数学記号の∑( ∑ )は素敵なことに別の文字で、 （実装によるのかもしれないんですが）後者は総和演算子扱いされて添え字が∑の真上と 真下につくんですが、うっかりそのつもりでギリシャ文字のΣを使うと悲惨なことに なっちゃったり。"代替文字"をちゃんとunifyしてくれれば無事なんでしょうが、さてさて。

続き

それでなんだっけ、一晩寝たらほとんど忘れたよ。 「コンパイラは簡潔に、アプリケーションコードも簡潔に、全てのノウハウ的なものは ライブラリ部分に突っ込む」のだという話でした。 他にはFortress言語自体のもうちょい詳しい話。

Traits ベースのオブジェクト指向 を採用する。

Traits の多重継承もOK。そういうのを活用して、「ライブラリによる プリミティブっぽい機能の実現」を実現していく感じ。（"Printable" とか "Immutable" と言ったTraitsを例に挙げてました。）

Traitsはフィールドを持たない。クラス階層（Traits階層？）の末端に来る具体的な 実装クラスにだけフィールドを置いてよい。

並列計算の扱い方はどんな風になるのか。

for i←1:m, j←1:n do a[i,j] := b[i] c[j] end

1:m や 1:n は "generator"。もちろん、コンパイラ組み込みの機能ではなくて、 ライブラリで定義される。

for ... do ... end は "reducer"。これももちろんライブラリによって定義される。

generatorがreducerを制御する、という関係になる。

標準ライブラリの一番スタンダードなgeneratorである 1:m は、Fortress が 計算用言語であることからして、各CPU各スレッドに処理をばらまいて並列にreducerを 実行する、というgenerator。どのように処理を分散するかetcはgeneratorのライブラリの コードとして記述されている。

並列実行generatorを受け取って逐次実行generatorを返すseqという関数があって、

for i←seq(1:m), j←seq(1:n) do a[i,j] := b[i] c[j] end

とすると、逐次実行されるループになったり。

reducerはもちろんfor文だけではなくて、(Σ[k←1:n] a[k]) の様に和を取るreducerや、 MAX...のように最大値を取るreducerなど色々考えられる。

seqのようなものは、各iterationが始まる前に直前のiterationが完了していることを ちゃんと保証しておかないといけないわけだけど？という質問があって、そう、そういう ことをライブラリでseq関数としてきちんと全部記述してやるのだ、という回答、だったと 思う（ちゃんと聞き取れてなかったかも）。

並列処理というと同期とかロック処理とかの話は…

データベースなどでよくあるような、トランザクションによる管理を基本とする。 atom do ... end というブロックで囲むと一つのトランザクション。

ここ数年、効率的にトランザクションを実現する技術はいい感じに進歩している。

SunのFortressも、IBMのX10 も、 CrayのChapel も、 同じ目的に向けて独立に作られている言語だけれど、どれもトランザクションを 採用している。時代はトランザクション。

そんなところで。