2008/09/02
100年後の言語
LL Futureにはかなり前から出演を 頼まれていたのだけれど、7月のキプロス行きの後で腰を痛めて、 今回もLA行きの直後に飛行機旅行が続くと腰が持つかどうか心配になり、 えんどうさん他スタッフに申し訳ないけれど辞退させてもらった。 (開催が近くなってからのキャンセルでご迷惑おかけしました)。 実際、体力的にきつかっただろうなと思う。
さて、blogをあちこち見ていたんだけれど、 「100年後の言語」セッションに関してとてもおもしろい問題提起が あったのでちょっと考えてみた。
それこそコンピュータアーキテクチャがノイマン型(プログラム内蔵方式のコンピュータ)で あるならば、どっかのレベルではメモリという概念が必要になってきて、 関数型のOSやらshellを用意したところで、どうしてもC的なレイヤがでてくる。
ここで言う「C的」とはハードウェアの状態変化によく対応しているという意味だと思う。 実際、C言語は長らくポータブルアセンブリとして活躍してきたわけだが、 ぼちぼちその抽象化がハードウェアに合わなくなっているんではないかという気がしている。
ハードを気にしてコードを書くなら、今や階層化されたメモリや メモリアクセスのリオーダリングなどを考えざるを得ないが、C言語そのものには そういった機構を明示的に扱う仕様は組み込まれていない。 その一方で、aliasing ruleを破っているコードは何の警告もなく直感に 反するような動作をする (e.g. strict aliasing rule)。
要するにCで書いてハードに直接触っているというのは全くの幻想で、 Cで書いててもプログラマはやっぱり抽象化されたモデルを扱っているにすぎず、 しかもそのモデルにはあちこち漏れがあるというわけだ。 (むしろ、怪しい動きがあれば開発中のREPLで直ちにdisassembleして ネイティブコードを確認するLispプログラマの方が、 ハードに近いかもしれない←単にコンパイラを信用してないだけとも。)
メモリとプロセッサのやりとりがどんどん非同期になっていって、 マルチコアやヘテロなコアになって、ひとつのファイル内で ある部分と別の部分は違うアーキテクチャのコアで実行されているなんて ことになってくれば、見た目はCでもハードからの距離はさらに遠いものに なってゆくだろう。
で、別にCが悪いとかそういう話ではない。 ただ、「C言語的」なモデルが「ハードに近いレイヤ」であったのは歴史の一時点の ことであって、C言語をこれからもそのように扱うのは多分間違いだ。 たとえノイマン型は変わらないとしても、スレッドやメモリバリア、 キャッシュ、ヘテロなコアなどを最初から考慮に入れて新たに 「ハードに近い、高級アセンブラ」を考えるとすれば、 違ったモデルが出てくるだろう。 そして、それが例えば疎結合なメッセージパッシングに基づいたものになったとしても 私は驚かない。その時C言語は、メッセージパッシングなプリミティブが エミュレートする「古き良き時代のCPUとメモリのモデル」上で走る高級言語になるかもしれない。
文法の独自拡張は、そのコミュニティを分断することになるのではないかと思ったりする。 つまりXXの機能を利用するためにYYという文法定義が必要で、 それが従来のZZとコンフリクトするので素直には利用できない。 ライブラリやパッケージでも同様な問題はおきなくはないが、 文法は通常一緒なので最悪コピペ的に回避することは不可能ではないが、 文法をいじってしまうと、それも簡単ではない。
これはLispのマクロに対して良く言われることと同根なのだけれど、 このような懸念はモジュラリティの問題であって文法の話とは全く関係がない。
ライブラリであってもグローバルな影響をもたらすライブラリ同士は 回避不可能なコンフリクトを起こす。 (動的言語でありそうなtrivialな例としては、演算子'+'の意味をグローバルに 変えてしまうようなライブラリを複数使いたい場合とか。 もっとnon-trivialな例として、ライブラリAとライブラリBが異なるGCを採用している場合とか。)
結局、文法だろうがライブラリだろうが、 モジュール機構などで「スコープを限定できること」が コンフリクト回避の鍵であって、スコープを限定できないようなものがあれば それは本質的にコンフリクト回避不可なのだ。
(ただし、ライブラリがリンクもしくは実行時に影響してくるのに対し、 文法の拡張はコンパイル時に影響を与えるので、依存関係の処理が若干面倒に なるのは確かだ。これはどっちかというと処理系作成者が悩むところだが)
Programming language design and security
I think programming language designers are not exempted from the responsibility of (at least trying to) making computer systems secure. If I could hear more constructive ways of solving the security problems in the answer from one of the panelists, such as:
- protecting the language from the buffer-overflow bugs;
- preventing the garbage collector from crashing even in a hostile environment where the external attackers try to deliberately manipulate the pointers or variables to hack into the protected memory area;
- implementing a syntax or semantics validation system in the programming language so that the programmers can apply it to validate the external data, such as those from the network-connected users; or
- preventing the race condition, deadlocks, or any sort of resource starvation by the intentional or unintentional programming;
then I would have been much more convinced. But now I should suspect that quite a few programming language designers just don't care about the security consequences of the features they build into the language.
こちらは、これからの言語はセキュリティを考えに入れないといかんでしょうという 問題提起。
で、私が考えるのは、セキュリティの問題というのは仕様と実装の齟齬の問題の 一形態であるということ (セキュリティポリシー(=仕様)を決めない限り、 何がセキュリティフローかは決まらない)。なので、究極の回答というのは 仕様からそれを満たすプログラムを合成するって話かなあと思う。 たぶんあろはさんが詳しそう。 (仕様をどう記述するのが良いかって問題は別に残るけど)。 ただそれが実用規模で実現するのはもうしばらくかかりそうだ。
なので、ポイントになってくるのは静的動的さまざまなプログラム検証って ことになると思う。
プログラム検証は検証で色々やられているが (例えば、上に引用した部分でrace conditionについて触れられてるけど、 Tachio Terauchi, Checking race freedom via linear programmingは実用規模のCプログラムに対して 静的解析でメモリアクセスに対する競合が起き得るかどうかを検証している)、 言語作成者としては「実用性を保ちつつ、かつ検証しやすい言語仕様とは何か」 を考えてゆくことになる。
鍵となりそうなアイディアはいくつか。
上でも挙げたけど、モジュラリティ。ある操作の影響範囲が明確に限定できて、それが 必要以上に大きくないこと。検証は範囲を限定すればするほどやりやすい。
なおモジュラリティという意味では、スレッド+グローバルなmutationというモデルは 最悪で、そこだけ見てればちゃんと動くはずのプログラムが、全然関係なさそうな ところの変更に影響を受ける可能性がある。なので言語としては別のモデルを 提供するのが吉。
関数型のモデルは状態の管理がローカルになるという点で強力な武器になる。 けれど、それとは別に複数の制御の流れを綺麗に扱えるモデルが必要だろう。 π計算みたいなものをうまく取り入れられるのか、それともlindaみたいに 同期機構をマクロに見るのがいいのか、そのへんはよく知らない。
ただし、これらの理論を純粋に採り入れて 制限をきつきつにしてしまうと今度は実用プログラムが書きづらくなる。 で、実用のために制限に穴をあけたくなるわけだが、無制限に穴をあけると そこで「何でもあり」になってしまう。 なので、下の層として「ハードに近いけれど頑健な」モデルがあるといい (それは間違ってもC言語じゃない)。上で挙げたようなメッセージパッシング かもしれないし、型付きアセンブラみたいなものが使えるかもしれないし、 そのへんは住井さんが詳しいと思うけれど、 under the hoodに降りてビットを直接いじってるんだけどそれが検証可能である、 というふうになって行くんじゃないだろうか。
もひとつ、これも上に挙げた文法のカスタマイズにつながるんだけれど、 問題のドメインがはっきりしている場合、それ用のDSLで書いてある方が 検証しやすい。たとえばAlan KayのとこでIan Piumartaらが やってるプロジェクトでは、RFCに書かれてるTCPの仕様をDSLとして解釈して コンパイルすると実際に動作するTCPプロトコルスタックになる、というのを 書いたそうだが(大島さん、間違ってたらツッコミお願い)、 ここまでやって問題が出たらそれは仕様のバグってことになるわけだ。 ここまでやらずとも、DSLを使うというのは、問題領域で使われる概念に良く対応する 言葉を組み合わせてプログラムするということだら、プログラマの意図がより明確であり、 その意図に照らした検証がしやすいと言えるだろう。
あともひとつ。これもIanの受け売りに近いんだけど、こうやってメタプログラミングを 極めて行くと複雑なシステムがえらくコンパクトに書けるようになる可能性がある。 実際、彼らはOS・言語処理系からアプリに至るまでの全てを20000行で書くという プロジェクトをやっていて、ネイティブコンパイラが1000行ちょい、とかは 既に実現されてる。 で、1000万行のシステムと2万行のシステムなら後者の方が検証ははるかに 楽だろう。1000万行のシステムの99.8%が機械的に除去可能な冗長性でない限り。 (そうそう、Ianのやつは上の「C的なレイヤ」の話の反例にもなってると思う。)
私としては未来はそっちの方向かなあという気がしてて、Gaucheのシステムも だんだんメタに記述していって記述量を減らして行きたいと思っている。
Tags: Programming, Gauche