FogBugz 4.0への道: パート3
From The Joel on Software Translation Project
Joel Spolsky / 青木靖 訳
2005年3月30日 水曜
2003年の夏、友人のウーデイがWindowsのサーバがあればFogBugzを買うんだけどと17回目に言ったとき、私はUnixバージョンについて考えはじめた。Chilisoftが出しているUnixで動くASPとVBScriptの実装を調べてみたが、当時それはサーバ1台につき1000ドルくらいかかり、それだけあればFogBugz用にWindowsマシンを買うだろう私たちの顧客に受け入れられる値段ではなかった。
ある朝、会社へ向かっているとき、そうだ、サマーインターンにPHPを出力するASPコンパイラを書かせてはどうだろう、と思いついた。そうしたらFogBugzのソースから自動的にUnixバージョンを生成することができ、ASPバージョンに機能を追加したときにはPHPバージョンにもそれが自動的に反映される。
なんとその同じ日に、ジミーという若者がオフィスに電話をかけてきた。彼はイタリアの大学の2年次を終えて戻ってきたところで、夏の間にする仕事を探していた。私が彼に(未発表の!)面接質問をすると(ネットで探しても出てこないよ)、1990年にMicrosoftでブライアン・マクドナルドから同じ質問をされたときに私が答えたのとまったく同じように彼は答えた。まったく同じようにはじめは間違っており、それから最後には理想的な回答にたどり着いた。ジミーはまったく私のクローンのようだった。
自分のことが分かっている私の判断は違っていたのだが、私たちは彼を夏の間雇うことにした。そしてFog Creekのサマーインターンプログラムがここに始まった。
ジミーはPHPもASPもJavaもよく知らなかったが、私たちがASPからPHPへのコンパイラの実装言語にはJavaがいいだろうと考えていたので、彼はそれを飲むことにした。私たちは数週間でどうにか動くものを手にすることができた。夏の終わる頃にはかなりちゃんとしたものになっていて、FogBugzも動かせるようになっていた。もっともジミーが大学に戻った後、あらゆる細かい点まで正しく動くようにするのにマイケルがさらに2ヶ月ほど作業する必要があったが。
私はこれをずっとASPからPHPへのコンパイラと呼んでいるが、多くの人が「それはトランスレータじゃないの?」とメールで言って来る。
説明しよう。
コンピュータサイエンスのジャーゴンでは、トランスレータというのはコンパイラなのだ。それは同じものを指している。同義語なのだ。
一般的な用法では、コンパイラは機械語を生成するものだとみんな考えているが、それは必ずしも機械語である必要はない。たとえばJavaと.NETのコンパイラはバイトコードを生成する。そしてCfrontと呼ばれるStroustrupが作った最初のC++コンパイラは、Cのコードを生成した。Cはポータブルで、C++をCコンパイラのある任意のシステムで動かせたからだ。新しい言語を作っているとき、あらゆるアーキテクチャごとの機械語が生成できるようにするのは時間の無駄だ。Cのコードを生成しさえすればよく、機械語の生成と最適化やそのほかの裏方の仕事はccに任せておけばいいのだ。Cfrontは、ときに「プリプロセッサ」と呼ばれていたが、それは多くの人にとってはなはだ混乱を招くことだった。C++は単なる気の利いたマクロのセットであり、昔ながらのCプリプロセッサであるcppによってCのコードを生成しているのだとみんな思っていたからだ。これは真実からはほど遠い。単純なCプリプロセッサでC++をCに変換するなんてことはできない。Cfrontは完全なコンパイラであり、コンパイラの基本的な要素をすべて持っていた。字句解析、パーサ、抽象構文木、そのほか。ただし1種類のCPUでしか動かせない機械語を出力するのではなく、恐ろしげだが完璧にポータブルなCのコードを出力したのだ。
そしてそれがASPからPHPへのコンパイラであるThistleのすることでもある。Thistleは正規表現の塊ではなく完全なコンパイラであり、VBScript/ASPプログラムの字句解析と構文解析をし、抽象構文木をメモリ中に構築し、「実行可能プログラム」を生成する。その実行可能プログラムがたまたまPHPだというだけの話だ。だから私がThistleを「コンパイラ」と呼んでいるのは簡単のためでもあるが、それよりもっと大きいのは、私が博識ぶったおしゃべり屋で、コンパイラは機械語を吐くものだと思っている若い連中にちょっとした教えを垂れてやれる機会を見逃せないからだ。
おしゃべりは横に置いておいて、Thistleについていつも聞かれるいくつかの質問にここで答えることにしよう。
Q. どうしてasp2phpを使わないの?
asp2phpが生成する「トランスレーション」のクオリティは、プロダクション品質のコードとはほど遠い。私たちが食わせてみたサンプルでは、生成されたコード行のほとんどすべてにエラーがあった。VBScriptとPHPには非常に微妙な所がいくつかあって、asp2phpはそれをまったく考慮していないように見える。
- (OK、本当に知りたい? VBScriptでは変数に配列を代入し、そのすぐ後同じ変数に整数型引数を1つとるデフォルトメソッドを持ったオブジェクトを代入することができる。だから式a(1)は最初の場合には$a[1]と解釈され、2番目のケースでは$a->$default(1)と解釈される。同じ式がだ。そしてこの根本的な問題を、たぶんそうする以外になかったのだろうが、asp2phpのデザイナは完全に無視しているのだ。このすぐ後に、私たちがこの問題をどう解決したか説明する。)
私たちには生成されたコードにそれ以上の修正が必要であってはならないという厳格な要求があった。ASP版のコードは生きていて絶えず修正されるため、PHPコードは絶えず自動的に再生成されることになる。Thistleの出力にわずかでも手作業での修正が必要となるなら、PHP版を得るための重荷は劇的に増えることになる。だから私たちはあらゆる種類の条件コンパイルの仕掛けを実装したし、また、コンパイラ自体を自分たちでコントロールしていることで、生成されたコードがまさに私たちの欲するものになるよう、あらゆる知恵を組み込むことができた。
Q. Thistleがそんなにすごいなら、どうしてそれを売らないの?
Thistleはただ1つのプログラム、FogBugzをコンパイルするためのコンパイラだ。このことはFogBugzが使わないロジックは含めなくともよいことを意味する。使わないので変換する必要がないライブラリ関数がたくさんあるのだ。それに私たちが常に守っているコーディング規則があるので、コンパイラはそれを近道として使うことができる。要はThistleを他のプログラムにそのまま使うことはできないのだ。
- a(1)をどう変換するかという問題を覚えている? これは実行時におけるaの型に応じて、「配列aの2番目の要素を参照する」こと、あるいは「オブジェクトaのデフォルトメソッドを引数1で呼び出す」ことを意味する。これは本当に大きな問題になる。私たちは配列を使っているし、組込みのRecordSetもそこらじゅうで使っており、そこではrs(1)はrs.Item(1).Valueを意味している。そしてVBScriptはレイトバインディングであるため、実行時でなければどういうPHPコードを生成したらいいのか分からないのだ! これをPHPにする唯一正しい方法は、aのタイプをチェックするコードを生成し、配列参照するかメソッド呼び出しするかを実行時に判断するということだ。これは面倒で遅く、配列が多く使われる実行回数の多いループなどでは実行時に大きな負担となる。
- 私たちはこの問題にどう対処したのか? ハンガリアン記法だ。上海-マグレブ鉄道の中で向こうからやってきても、それが優れたコーディング規則であることを知らない開発者たちは冷たくあしらい、ズボンのすそをはたくあれだ。Fog Creekのレコードセットはすべて"rs"で始まっている。Thistleはrsを見つけると、「ああ、これはレコードセットだ。配列の値を参照しているのではなく、デフォルトメソッドを呼んでいるのだ」と言い、速いコードを生成する。あなたの年代に応じて、こういうのを悪いハック(あなたが若いなら)と呼ぶかもしれないし、エレガントなハック(あなたが年嵩なら)と呼ぶかもしれない。どちらにせよ、Thistleがコンパイルすべきプログラムが1つしかないことによって、大幅な最適化が可能になるのだ。Fog Creekの外ではこれは機能しない。ハンガリアン記法万歳!
私たちがThistleを商用化しない理由がもうひとつある。これは1つのソースツリーからWindows版とUnix版(あるいはMac版、Solaris版、Linux版)のサーバサイドアプリケーションを作れるようにしてくれる、私たちにとっての競争優位の源なのだ。まあこれは二義的なことだが。
Q. ハンガリアン記法の話は本気なの?
その通り、Fog Creekではハンガリアン記法が標準だ。私たちが使っているのはシモニイにより考案されたアプリケーションハンガリアンと呼ばれるもので、ペゾルドやWindowsチームによる誤解の産物であるグロテスクなシステムハンガリアンではない。シモニイのオリジナルの論文は今でも読むことができる。
- アプリケーションハンガリアンにはプレフィックスがあるが、これは意味論的な情報を与えることを意図しており、単なる変数の型を繰り返すものではない。たとえばバッファサイズを表す変数があれば、それはcbと名づけられるべきで、これはcount of bytes(バイト数)の意味だ。C++のコードを書くときには、あらゆる種類の文字列を扱う必要があるので、変数名を見て、それがたとえばpszなら、ああ、これは0終端文字列だがメモリは割り当てられてないんだと分かるのは、とても助けになる。あるいはrgchで始まるならメモリを割り当てられた文字配列だと分かり、rgwchならUnicode(wide)文字配列だと分かり、ixで始まるなら何かのインデックス(あるいはSQLでの主キー)だとわかる。Unicodeを扱っているときにはこれは特に都合が良く、cchはcount of chars(文字数)を意味する一方、cbはcount of bytes(バイト数)を意味し、1文字が1バイトでないことによって混乱しないよう、変数名ではっきりさせることができる。それから文字列へのポインタを使っているのか文字列へのポインタへのポインタを使っているのか覚えていなくとも、頭についているのが"p"か"pp"かで識別できる。なんにしても、ハンガリアン記法は、すべてのlong型変数にdwをつけなきゃいけないことの意味が分からない人たちから、広く、もっともな批判を受けているが、かつてはそれでさえ役に立ったのだ。しかし個数に"c"を使い、インデックスに"ix"を使うというのははるかに有用だ。
- SQLのテーブルではフィールドの型を表すのにライト版のハンガリアン記法を使っている。sは文字列、dtは日付時刻、cは個数、ixはキー、nは個数以外の数か、ある種のキーに使っている。これのもっとも有用な側面は、テーブルの名前がBugならその主キーはixBugと分かり、データベースのどこかのSQLでixBugを見たら、定義を探すまでもなくそれがBugテーブルを指す外部キーだと分かる。「join Bar on Foo.ixBar = Bar.ixBar」というスニペットを見たら、考えるまでもなく何をしているのかがわかる。そして「join Bar on Foo.ixFred = Bar.ixBar」というコードを見れば、それがおそらく誤りだということがわかる。すこし慣れればixFred/ixBarのようなミスマッチは目に飛び込んでくるようになる。
- ハンガリアン記法はコードをかえって読みにくくするとブログで吹聴し続けている人たちを見ると特に頭にくる。確かにこの記法を知らないなら読みにくくなるだろう。しかし記法を学ぶのに10分も使えば、重要なプレフィックスはすべて覚えられるのだから、ハンガリアン記法がコードを読みにくくすると文句を言っている人たちは、明らかにそういう努力をしたことがないのだ。プレフィックスが何を意味するのか知っていれば、ハンガリアンはコードを読みやすくし、変数の定義を探し回らなくとも、それぞれの変数が有用な情報をただで与えてくれるのだ。
Q. ASPのランタイムライブラリはどう処理したの?
私たちは再実装した。後でPHPに変換されることになるVBScriptで実装したものもあるし、直接PHPで実装したものもある。たいていの場合、ほとんど同等のPHPの関数があって、一行のコードで実装できた。
Q. COMオブジェクトはどうしたの?
これはもっとも手間のかかった部分だ。私たち自身で作ったCOMオブジェクトについては、ポータブルなC++で書き、それをUnixの.soにコンパイルした。私たちの使っているサードパーティ製のCOMオブジェクトの1つは、組込みのライブラリを使いPHPで完全に再実装した。Server、Response、Request、Recordsetといった組込みのオブジェクトについては、対応するクラスを自分で作ったが、多くは下にあるPHPの機能を呼び出しているだけだ。私たちのServer.CreateObjectの実装はタネを全部知っており、しかるべき種類のオブジェクトを生成する巨大なselect文として実装されている。だからCOMクラスファクトリと等価なものは必要とならない。
Q. その価値はあった?
たぶん。Unix版のFogBugzは売上の10%ほどとなっている。それを作るための労力は、Windows版だけつくる場合の労力の20%ほどだ。Windows版を使っている顧客も、Unix版を提供し始めたことを喜んでおり、それは彼らがいつでもサーバを切り替えられるようになったからだ。私たちもまた、MicrosoftがサーバOSでのVBScript/ASPのサポートをやめたりしないか気にかけなくて済むようになった。Thistleが今週の新しいフレーバーを生成するよう変更するだけのことだ。それがRuby.NETであれ、Eiffelであれ、C#であれ、あるいはこの言語であれ。
明日は、FogBugz 4.0を単なるコンピュータプログラムではなく、完全な製品とするべく、私たちがしたその他のことについて話す予定だ。
(オリジナル: The Road to FogBugz 4.0: Part III)
