技術野郎の復讐---Revenge of the Nerds---[訳註1]

Paul Graham, May 2002.
Copyright 2002 by Paul Graham.

これは、Paul Graham:Revenge of the Nerds を、原著者の許可を得て翻訳・公開するものです。

プロジェクト杉田玄白正式参加テキスト。

<版権表示>
本和訳テキストの複製、変更、再配布は、この版権表示を残す限り、自由に行って結構です。
(「この版権表示」には上の文も含まれます。すなわち、再配布を禁止してはいけません)。
Copyright 2002 by Paul Graham
原文: http://www.paulgraham.com/icad.html
日本語訳:Shiro Kawai (shiro @ acm.org)
<版権表示終り>

Paul Graham氏のエッセイをまとめた『ハッカーと画家』の 邦訳版が出版されました。
出版社の案内ページ Amazon.co.jp サポートページ

2002/05/30 翻訳公開
2002/06/02 yomoyomoさん、 馬目さん、およびYuさんより誤記の指摘を頂き、訂正。あと、訳註を追加。
2002/06/10 プロジェクト杉田玄白正式参加に伴い版権表示を整備
2002/10/15 Erann Gatの"How I lost my faith"の和訳をリンク
2003/04/16 "How I lost my faith"の和訳のURL変更に追従
2004/03/21 Erann Gatの"Lisping at JPL"に関する情報を追加


(この記事は2002年5月に国際ICADユーザー会会議で行った基調講演の内容に 加筆したものである。 この記事では、1958年に作られた言語がなぜ現在でも最もパワフルなのか、 そのパワーとは何か、なぜ必要とされるのか、 そして髪のとんがった上司[訳註2]が(理想的には、もちろんあなたのライバルの上司だが) なぜこの事実を意図的に無視するのかについて説明する。

注記: この講演において、私は "Lisp" という用語を Common Lisp、Scheme、Emacs Lisp、EuLisp、Goo、Arc等の Lispファミリーの言語の総称として使っている。


最初に、ICADについてじつはあまり知らないんだということを断っておこう。 でもそれがLispで書かれていることは知っているし、それがLispを含んでいる、 つまりユーザがLispプログラムを書いて走らせることができるってことは知っている。

Lispで書かれたプログラムがLispを「含む」のはよくあることだ。 Emacsがそうだし、Yahoo Storeもそうだ。 でもちょっと考えてみると、これはずいぶん奇妙なことだ。 いったいいくつのCで書かれたプログラムがCを「含んでいる」、 つまりユーザがそのプログラムを使っている間にCコンパイラを走らせたりしているだろう。 Unixをアプリケーションの一つとでも考えない限り、私にはちょっと思い付かない。 まだこの話を初めて1分も経ってないのに、もうLispの変てこりんなところが 見えて来た。

いや、もちろんここにいる皆さんはLispが変てこりんだってことは 先刻承知のことと思う。実際、初めてLispを見た人が真っ先に気づくのがその奇妙さだ。

信じられないかもしれないけど、Lispのコードがそんなに奇妙に見えるのには 理由があるんだ。何も、頭のとんがった学者達が設計したからこんなふうに なっちゃったというわけじゃない。 いや、Lispは確かに頭のとんがった学者達が設計したんだけど、 その構文がこんなに変になったのには確固たる工学上の理由があったからなんだ。

全ての言語は等しいのか?

ソフトウェアビジネスでは、頭のとんがった学者達と、 同じくらい手ごわい勢力である髪のとんがった上司とが絶え間無い抗争を繰り広げている。 髪のとんがった上司のことはみんな知ってるよね。 テクノロジー業界にいる人なら大抵はこのマンガのキャラクターだけでなくて、 そのモデルになりそうな現実の人間を社内に見付けられると思う。

髪のとんがった上司は二つの性質を持っている。 どちらの性質もごく普通にみられるけど、 両方とも兼ね備えるとなると奇跡的だ。その性質とは: (a) 彼はテクノロジーに関して何も知らない。そして (b) 彼はテクノロジーに関して非常に強い意見を持っている。

例えば、あなたがあるソフトウェアを書かなければならないとする。 髪のとんがった上司はそのソフトウェアがどう動かなければならないか なんて全く理解できないし、あのプログラミング言語とこのプログラミング言語の 区別もつけられない。でも不思議と、彼はあなたが使わなければならない言語を 知っているんだ。その通り。Javaを使いなさい、ってこった。

何で彼はそう考えるんだろう。この髪のとんがった上司の頭の中を覗いてみよう。 きっとこんなふうに考えているんだろう。Javaは標準だ。 そうに違いない。だって新聞や雑誌にいつでも載っているじゃないか。 それが標準なら、それを使っておけば問題は起こるまい。 それにいつだってJavaプログラマはたくさんいるだろうから、 今働いている部下が辞めてしまっても、 いや、私の部下は不思議とよく辞めるんだが、それでもすぐ代わりが見付けられると いうものさ。

ふーん。確かにこれはそんなにおかしな考えとも思えない。 ただこの考えは一つの仮定に基づいていて、実はその仮定は間違いなんだ。 髪のとんがった上司は全ての言語はだいたい等しいと信じている。 これが真実なら彼は的を射ている。言語が全て等しいなら、 他のみんなが使っている言語を使いたまえ。

実際には、言語は等しくないんだ。 各言語の違いに立ち入らなくたってそれを証明することができる。 1992年にかの髪のとんがった上司にどの言語でソフトを書くべきか尋ねたら、 彼はこんにち彼がそうしたのと同じように、ためらいなく答えただろう。 ソフトウェアはC++で書くべきだ。 でも全ての言語が等しいなら、なぜ髪のとんがった上司の意見は変わるんだい。 それよりも、なぜJavaの開発者たちはわざわざ手間をかけて新しい言語を作ったりするんだい。

新しい言語を作るってことは、既に他の人がやったやり方よりうまく出来ると 思うからそうするんだろう。実際、Goslingは最初のJavaのホワイトペーパーで JavaはC++にあるいくつかの問題を解決すべく設計されたとはっきりと述べている。 ほらね。言語は全て等価ではない。髪のとんがった上司の頭からJavaへと、 そしてJavaの歴史をその起源へと辿ってみれば、最初の仮定と矛盾する考えに ぶちあたるというわけさ。

じゃあどっちが正しいんだろう。James Goslingか、髪のとんがった上司か。 もちろんGoslingが正しい。いくつかの言語は、特定の問題においては、 他の言語に優る。Javaは特定の問題においてC++より良くなるべく設計された。 どんな問題に? どんなときJavaはC++より優れているんだ? この2つの言語以外の言語が優れるような状況ってあるだろうか。

ひとたびこの問題を考え出すと虫の一杯詰まった缶を開けることになる。 髪のとんがった上司がこの問題をまともに考え出したら、彼の頭脳は爆発して しまうだろう。全ての言語は等しいと考えておけば、一番勢いのある言語を 選べばいいだけだ。それは技術よりはむしろ流行の問題なんだから、 そうやって選んだものが正しいかもしれないじゃないか。 でも言語が等しくないとすると、彼は二つの連立方程式を解いて 自分が全く知らない二つのことがらの間の最適なバランスを見つけ出さなくちゃならない。 20かそこいらある最新の言語の解くべき問題に対する相対的な適合性と、 各言語に対してライブラリやプログラマを見付けられる確率とだ。 そんなものがドアの向こうにあるのなら、 髪のとんがった上司がドアを開けたがらないのも無理はない。

全ての言語が等価だと信じることのデメリットは、それが真実ではないということだ。 だがそう信じることには、人生がよっぽど単純になるというメリットがある。 私が思うに、これこそがこの考えが広まっている主たる原因だ。 安心できる考えなんだ。

Javaはとても良い。だってクールで新しい言語だからね。 ほんとにそうかい? プログラミング言語の世界からちょっと離れて見てみれば、 確かにJavaが最新のものに見える。 (もっとうーんと離れて見たら、 Sunが出してるぴかぴかのでっかい看板しか見えないだろうけどね)。 でもこの世界に近付いてよーく見てみれば、クールさにもいろいろあることが 見えて来る。ハッカーのサブカルチャーの中では、Perlと呼ばれる言語があって Javaよりずっとクールだと考えられている。例えばSlashdotはPerlで生成されている。 あれをやっている連中がJava Server Pageを使うとは思えない。 そしてさらにPythonと呼ばれるもっと新しい言語があって、 そのユーザはPerlを見下しがちだ。さらに多くの言語が舞台袖で出番をうかがっている。

Java、Perl、Pythonと順に見て来ると、おもしろいパターンに気づくだろう。 少なくともあなたがLispハッカーであればきっと気づくはずだ。 各言語は次第にLispに近付いてきている[訳註3]。PythonはLispハッカーの多くが 間違いだと思っている機能さえコピーしている。簡単なLispプログラムなら、ほぼ 一行毎にPythonに変換することすら可能だろう。 2002年の今になって、プログラミング言語はようやく1958年に追い付こうとしている。

数学に追い付く

Lispは1958年にJohn McCarthyによって最初に発見された。 そして、人気のあるプログラミング言語は彼が当時発展させたアイディアに やっと追い付いてきた。

こんなことってあり得るだろうか。 コンピューター技術はものすごく速く変化しているんじゃないのか? 1958年と言えば、コンピューターは冷蔵庫くらいばかでかい化け物で、 でも腕時計くらいの計算能力しか持っちゃいなかった。 そんな古い技術が今でも使えるということでさえ驚きなのに、 最新の開発技術よりも優れているなんてことがあるんだろうか。

あるんだそれが。なぜなら、 Lispはプログラミング言語として設計されたんじゃなかった。 少なくとも我々がこんにち使うプログラミング言語と言う意味では。 つまりコンピューターに何をすべきかを指示するもの、という意味ではね。 McCarthyはその後確かにそういう意味でのプログラミング言語を作ろうとしたけど、 こんにちのLispになったものは彼が 理論的な実験としてやったもの、 チューリングマシンのより便利な代替物を定義しようとした試みの結果なんだ。 McCarthyは後にこう語っている。

Lispがチューリングマシンよりも扱いやすいモデルであることを示す もう一つの方法は、万能Lisp関数を書いてそれが万能チューリングマシンの 表記よりも簡潔で分かりやすいことを示すことだった。 これがLisp関数evalだ…、 それはLisp式の値を計算する。… evalを書くために、私はLisp関数をLispのデータとして表す方法を 考えなければならなかった。その表記はあくまで紙の上で確認するためのもので、 実際にLispプログラムがそれで書かれるようになるなんて考えもしなかったよ。

次に起こったのはこうだ。1958年後半に、McCarthyの大学院生のひとりであった Steve Russellがevalの定義を見ていて、これを機械語に変換すれば Lispインタプリタができるじゃないか、と思いついたんだ。

これは当時としては画期的なことだった。 McCarthyは後にインタビューでこのように語っている。

Steve Russelが言った。ほら、evalをプログラムしたらどうだろう…、 で、私は答えたものさ。はは、君は理論と現実を混同しているよ。 このevalは人が読むためのもので、計算するためのものじゃない。 でも彼はそのアイディアを進めて実際にやってしまった。 つまり、彼は私の論文のevalを[IBM] 704の機械語に直して、 バグを取り、それをLispインタプリタと称したんだ。 実際そうだった。それが、Lispが現在の姿を取った瞬間だった。

突如として、おそらく数週間のうちに、 McCarthyは彼の理論的な実験がプログラミング言語に---それも、 おそらく彼が意図したのより強力な言語に---変化したのに気づいたのだ。

だから、なぜ1950年代の言語が時代遅れにならないかという簡単な説明は、 それが技術じゃなくて数学だったということだ。数学は色あせない。 Lispを比較する対象は1950年代のハードウェアでなく、 例えばクイックソートのアルゴリズムだ。それは1960年に発見され、 いまだに汎用ソートアルゴリズムとして最速だ。

1950年代から今まで生き延びている、もうひとつの言語がある。 Fortranだ。それはLispと正反対の方法で設計されている。 Lispは理論の一片が意図せずにプログラミング言語になったものだ。 Fortranはプログラミング言語となるべくして設計されたが、 現代の我々の基準からすれば非常に低レベルなものだった。

1956年に作られたFortran Iは 現在のFortranとはずいぶん違う生物だった。Fortran Iはアセンブリ言語に 数学がちょっとくっついたものだった。ある意味、現代のアセンブリ言語よりも 弱いと言えるかもしれない。例えばサブルーチンは無く分岐だけがあった。 現代のFortranは、Fortran IよりはかなりLispに近い。

LispとFortranは二つの独立した進化系統樹の幹にあたる。 一方は数学に根ざし、もう一方はマシンアーキテクチャに根ざしている。 これらの二つの樹は発生以来、常に近付いてきた。 Lispはパワフルな言語として出発し、以降20年の間に速度を獲得した。 いわゆる主流の言語は高速なものとして出発し、以降40年の間に徐々にパワーを得て来た。 いまや、最も進んだ言語はLispにかなり近い。 近い、が、まだいくつか欠けているものがある…

Lispのユニークさ

最初に開発された時、Lispは9つの新しいアイディアを内包していた。 そのうちのいくつかは現代では当然のことになっている。 残りのうちいくつかは進んだ言語に見られ、2つはまだLisp特有だ。 主流の言語に取り込まれた順にこの9つのアイディアを並べるとこうなる。

  1. 条件式。if-then-elseという構造だ。今ではあって当然のものだが、 Fortran Iには無かった。Fortran Iが持っていたのは機械語の命令を反映した、 条件付きgotoであった。

  2. 関数型。Lispでは関数は整数や文字列と同じようなデータ型のひとつだ。 リテラル表記を持ち、変数に代入できて、引数として渡せる、というようなことだ。

  3. 再帰。Lispは再帰をサポートした最初のプログラミング言語だった。

  4. 動的型付け。Lispでは全ての変数は実質的にポインタだ。 変数ではなく値の方に型が付いており、 代入や束縛は実質的にポインタのコピーで行われ、ポインタが指されるものには作用しない。

  5. ガベージコレクション。

  6. 式でプログラムが構成されること。Lispプログラムは式の木であり、 それぞれの式が値を返す。これはFortranとそれを引き継ぐ多くの言語とは 対照的だ。それらの言語は式と文を区別している。

    Fortran Iにおいて式と文を区別することは自然だった。文はネストできなかったからだ。 数式が使えるようにするのに式は必要だったが、 それ以外の処理が値を返す必要も無かった。 文の戻り値を受けるものなんて無かったからだ。

    この制限はブロック構造化言語の到来によって無くなるはずだったが、 その時には既に手遅れだったのだ。式と文の区別は慣行の中に根を下ろしてしまっていた。 それはFortranからAlgolに広がり、その子孫の言語へと受け継がれて行った。

  7. シンボル型。シンボルは実質的にはハッシュテーブルに格納された文字列へのポインタである。 これによって、文字をひとつひとつ比較しないでもポインタの比較だけで済む。

  8. シンボルと定数の木によってコードを表現すること。

  9. 言語の全てが常に在ること。読み込み時、コンパイル時、実行時が 明確に分離したステップになっていない。読み込み時にコンパイルして コードを走らせることもできるし、コンパイル時にコードを読んだり 走らせたりすることもできるし、実行時にコードを読んだりコンパイルしたり することができる。

    読み込み時にコードを走らせることにより、ユーザがLispの構文を変更することが できる。コンパイル時にコードを走らせるというのはマクロの基本だ。 実行時にコンパイルするというのはLispをEmacsのようなプログラムの拡張言語として 使うことを容易にする。そして、実行時に読み込みができるというのは、 プログラム同士がS式を使って通信できるということだ。 最後のアイディアは最近XMLとして再発明された。

初めてLispが姿を現したとき、これらのアイディアは 1950年代後半に使えたハードウェアによって大きく制限されていた 当時のプログラミング技術からみてあまりにかけ離れていた。 時が経つにつれ、普通に使われる言語、 それはその時その時の人気のある言語で置き換わってきたが、 それはLispへと次第に進化してきた。 1〜5までのアイディアはいまや広く認知されている。 6番は主流の言語に現れ始めている。Pythonは7に相当するものを持っているが、 そのための構文は無いようだ。

8番はこの中でもっとも面白いものかもしれない。8番と9番のアイディアは、 McCarthyが実装するなんて考えもしなかったことをSteve Russellがやってのけたために、 全く意図せずにLispに入ったものだ。 それが結局、Lispの奇妙な外見と、それを最もユニークたらしめている理由になった。 Lispが奇妙に見えるのは、奇妙な構文を持っているからというより、 構文を持っていないからと言ったほうが良いだろう。 普通の言語なら舞台裏で構文解析して作られるような構文木を、 Lispプログラマは直接書いてプログラムを表現する。 その構文木はリストで表現され、それはLispのデータ構造だ。

言語を自分自身のデータ構造で表現するということは、非常に強力な機能となった。 アイディア8と9を合わせると、プログラムを書くプログラムを書けるということになる。 これは突飛なアイディアに聞こえるかもしれないが、 Lispプログラマが日常的に行っていることだ。 このアイディアは通常、マクロと呼ばれる方法で実現される。

Lispにおける「マクロ」という用語は他の言語におけるそれとちょっと違う。 Lispのマクロは、単なる省略形から新しい言語のコンパイラまで何にでも成り得る。 もしLispを本当に理解したいか、プログラミングの認識の地平線を広げたいなら、 マクロについて学ぶことをお勧めする。

(Lispの意味での)マクロは、私の知る限り依然としてLispに特有のものだ。 たぶん、マクロを持つためには言語をLispと同じような奇妙な外見にしないと 駄目だからだろう。それにまた、マクロという最後の力を加えたら、 それは新しい言語ではなくLispの新しい方言になってしまうからだろう。

私はよくジョークでそういうことを言うのだが、実際それは真実なんだ。 car、cdr、cons、quote、cond、atom、eq、そして関数をリストで表現する 方法を備えた言語があれば、あなたはLispの残り全てをそれで構築することができる。 これがLispの質を定義しているんだ。McCarthyがLispにこの姿を与えたのは、 まさにそのためだったからだ。

言語が問題になるとき

では、Lispが主流の言語が漸近する極限にあると仮定してみよう。 そうだとしても、その言語を使うべきなのだろうか。 より力の弱い言語を使ったら、失うものとは何だろうか。 場合によっては、最先端にいることを避ける方が賢明なこともあるんじゃないか。 それに、人気があることはそれ自体で正当化されないか。 例えば髪のとんがった上司がプログラマを簡単に見付けられるような言語を使いたがるのは、 それはそれで正しいことなんじゃないか。

もちろんプログラミング言語の選択がそんなに問題にならないプロジェクトというのはある。 基本的に、アプリケーションの要求が高ければ高い程 力のある言語を使って得られるものは大きくなる。 だがこの世にある多くのプロジェクトの要求はさして高くない。 おそらくプログラミングのほとんどは、既存の部品をつなぎ合わせる小さな糊付けプログラムを 書くようなものだし、それならもう手に馴染んでいて、目的に合わせたライブラリが 充実している言語を使えばいい。Windowsの一つのアプリケーションから 別のアプリケーションへデータを流したいだけなら、そりゃあVisual Basicを使えばいいのさ。

Lispでも小さな糊付けプログラムを書くことはできる (私はデスクトップで電卓の代わりに使っている)。 でもLispみたいな言語が一番威力を発揮するのはプログラミングのスペクトルの逆の端、 激しい競争に直面しつつ難しい問題を解く洗練されたプログラムを書かなくちゃならない時だ。 ITA SoftwareがOrbitzにライセンスした 航空料金検索プログラムが良い例だ。 彼らは、既に2つの大きな競争相手、TravelocityとExpedia がシェアを確立した業界に参入して、技術的に彼らの鼻をあかしたのだ。

ITAのアプリケーションのコアは200,000行のCommon Lispプログラムで、 おそらくメインフレーム時代のプログラミングテクニックを使っている競争相手の 何桁も上の組み合せの中から検索を行う (ITAもある意味ではメインフレーム時代のプログラミング言語を使っていると言えるのだが)。 私はITAのコードを見たことはないが、かの会社のトップハッカーの言によれば、 彼らはたくさんのマクロを使っているそうだ。そうだろうと思う。

求心力

広く使われていない技術を使うのにデメリットが無いというつもりはない。 かの髪のとんがった上司も、その点を心配するのには一理ある。 ただそのリスクを正しく理解しない限り、彼はリスクを過大評価しがちだ。

私が思うに、一般的でない言語を使うことから生ずる問題は3つある。 そのプログラムは別の言語で書かれたプログラムと一緒にはうまく 動かないかも知れない。すぐ使えるライブラリが少ないかもしれない。 そして、プログラマを見付けるのが難しいかもしれない。

それぞれはどのくらい問題となるだろうか。最初の問題の重要性は、 あなたが全体のシステムをコントロールできるかどうかによって異なって来る。 あなたのソフトウェアが、離れたユーザーのマシンで、 クローズドでバグの多いOS (敢えて名前は挙げないが) の上で走らねばならないのだ としたら、OSが書かれているのと同じ言語でアプリケーションを書くことに 利点はあるだろう。だが全てのシステムがあなたの制御下にあり、 全ての部分のソースコードを持っているなら(おそらくITAはそうだ)、 あなたは望みの言語を使うことができる。互換性に問題が生じたら自分で直せるからだ。

サーバーベースのアプリケーションではあなたは最新の技術を使うことができる。 これが、Jonathan Ericksonが「プログラミング言語の ルネッサンス」と言った主要な理由だと思う。 PerlやPythonといった新しい言語がポピュラーになったのもまさにそのためだ。 これらの言語は、人々がそれでWindowsアプリケーションを書いてるから評判に なってるんじゃない。サーバーでそれらが使われているから評判になっているんだ。 そしてソフトウェアがデスクトップから離れてサーバーに 移行してゆくにしたがって(マイクロソフトでさえその流れに乗ろうとしている)、 主流の技術を使わねばならないというプレッシャーはどんどん減って行く。

ライブラリに関しても、その重要性は書くアプリケーションに依存する。 さほど難しくない問題に対しては、ライブラリがあることは言語に備わった 本質的な力よりも重要なことだろう。どこで両者の重要性が逆転するだろう? 正確に言うのは難しいが、それがどの点であろうとも、アプリケーションと 呼べるものに達するちょっと手前であると思う。 もしある会社がソフトウェアビジネスで生きて行こうと考え、 その製品としてアプリケーションを書き始めたとしたら、 少なくとも数人のハッカーで半年かそこいらの期間は必要とするはずだ。 そのくらいの大きさになれば、既存のライブラリがあることの便利さよりも、 言語の力の方が重要になってくるだろう。

髪のとんがった上司の3番目の懸念、プログラマを見付ける難しさに関しては、 杞憂だと思う。結局のところ、何人のハッカーが必要なんだい? もう今となっては、ソフトウェアは10人以下のチームで書くのが一番だって みんな知っているだろう。そしてどんな言語であっても、 その言語で雇われたプログラマがかつて存在したような言語なら、 そのくらいの人数のハッカーは集められるはずだ。 10人のLispハッカーが集められないようなら、 たぶんあなたの会社はソフトウェアを開発するのに適さない都市にいるんだ。

実際、より力のある言語を選べば、必要なチームの人数を減らすことができるだろう。 (a) 言語がパワフルであればそんなに多くのハッカーは必要無いだろうし、 (b) より進んだ言語を操るハッカーは一般に能力が高いからだ。

「標準」と見なされている技術を使うべきだ というプレッシャーを全く受けないかと言えばそれは嘘になる。 (現在はYahoo Storeとなった)Viawebでは、 Lispを使っていることでベンチャーキャピタルや会社を買いに来た人々の目を 丸くしたものだ。でも、「実務に耐える」Sunのようなサーバーでなく 普通のIntel boxをサーバーに使っていたことも、 Windows NTのような本物の商用OSでなく当時はまだあまり知られていなかった オープンソースのUnixであるFreeBSDを使っていたことでも、 またe-commerceの標準になると思われていた SET (誰も覚えちゃいないと思うが)を無視したことでも、 ベンチャーキャピタルの目を丸くしたのだ。

スーツを着た連中に技術に関する決断をさせちゃだめだ。 我々がLispを使っていることで会社の買収候補者は警戒しなかったかって? そりゃ多少はね。でももし我々がLispを使わなかったとしたら、 そもそも彼らが買いたいと思うようなソフトウェアを書くことさえ出来ていなかったんだから。 彼らが異常だと思うことは、実は単なる原因と結果に過ぎない。

ベンチャーを立ち上げるなら、ベンチャーキャピタルや会社の買収候補者を 喜ばせようと思って製品をデザインしちゃだめだ。 ユーザーを喜ばせるようにデザインしなきゃ。 ユーザーを獲得すれば、全ては後からついてくる。 ユーザーがいなけりゃ、技術の選択がどれだけ主流に忠実かなんて誰も気にしやしないさ。

普通であることのコスト

じゃあ、力の弱い言語を使うことによってどれだけ損をするんだろう。 これに関しては多少データがある。

力をはかる一番簡単な方法はたぶんコードサイズだろう。 高レベル言語を使う理由は、より大きな抽象化を可能にしてくれることだ。 より大きな煉瓦を使えば必要な壁を作るのに少ない煉瓦で済む、というようなものだ。 だからよりパワフルな言語を使えばプログラムは短くなる (もちろん文字数だけでなく、言語の要素に関してもだ)。

よりパワフルな言語を使って、どうやって短いプログラムが書けるんだろう。 一つのテクニックとして、もし言語がそれを許すなら、 ボトムアッププログラミングと呼ばれるものがある。 アプリケーションをベース言語で書くかわりに、まず自分の書きたいプログラムに 適合する言語をベース言語で書いて、次にその言語を使ってプログラムを書くんだ。 両者を合わせたコードサイズは全てをベース言語で書いた場合よりずっと小さくなるはずだ ---実際、これは圧縮アルゴリズムの動作原理と同じだ。 ボトムアッププログラムは変更するのも楽だ。 多くの変更は言語の層に手を加えなくても済ませられる。

コードサイズは重要だ。プログラムを書く時間はプログラムの長さに大きく依存する。 プログラムが別の言語を使ったら3倍のサイズになるんだとしたら、 それを書くのに3倍の時間が必要になるだろう。そして、 それはプログラマを増やすことではカバーできない。 あるサイズを越えたら、新しく人員を投入するのは全体として効率を下げるだけになる。 Fred Brooksがこの現象を有名な「人月の神話」で述べているが、 私が見聞きした全てのことは彼の言うことを裏付けている。

じゃあ、Lispで書いたらどのくらいプログラムが短くなるだろう。 私が聞いたことがあるのはほとんどLispとCの比較だが、例えば、だいたい 7〜10倍という数字だ。だが、最近の New Architect 誌に掲載されたITAの記事には 「一行のLispは20行のCコードを置き換えられる」とある。 この記事はITAの社長の談話を多く載せているから、 たぶんこの数字もITAから出て来たものだろう。 だとしたらある程度信用できる。ITAのソフトウェアはLispだけでなく CやC++のものも多いから、彼らは経験に基づいて語っているのだ。

この倍率は定数ではないかもしれない。難しい問題になればなるほど、 そしてプログラマが優れていればいるほど、倍率は増加するだろう。 良いハッカーであればあるほど、良いツールをもっと活用することができる。

その曲線の一点として、もしあなたがITAとあるソフトウェアで競争するとして、 あなたがCを選んだら、彼らはあなたの20倍速くソフトウェアを開発できるということだ。 新しい機能を入れるのにあなたが1年を費したら、彼らはそれを3週間で追加するだろう。 彼らが3ヵ月かかって新しいものを開発したとしたら、あなたは5年かけなければ それに追い付けない。

そしてだよ。それはベストケースの想定だ。 こうやってコードサイズの比較をしている時は、 一応弱い言語でもソフトは書き上げられると仮定している。 でも現実には、プログラマができることには限界があるんだ。 低レベルすぎる言語で難しい問題を解こうとしたら、 ある点で頭の中に納めておくには多すぎる問題を抱えることになる。

先程のITAの仮想の競争相手が、ITAがLispで3ヵ月で書いたものを5年かければ コピーできると言ったのは、あくまで全てがうまく行った場合の話だ。 実際には、多くの会社では、5年もかかるようなプロジェクトはまず完成しない。

もちろんこれは極端な例だ。ITAのハッカー達はものすごく優秀だし、 Cはあまりに低レベルな言語だ。だが競争の激しい市場では、2〜3:1という 差であっても、ライバルにおいて行かれるには十分な数字だ。

レシピ

髪のとんがった上司にとって、こんなことは考えたくもないことだろう。 だから大抵の上司は考えない。結局のところ、彼らは会社が多少どうなろうと 自分の責任にならなければ良いのだ。彼にとって安全なプランとは、 なるべく群の中心に近い位置にいることだ。

大きな組織では、このアプローチは「業界の最良慣行(Industry best practice)」 という言葉で表される。 その目的は髪のとんがった上司を責任から護ることだ。 彼が「業界の最良慣行」である何かを選んでそれで会社が競争に負けたら、 悪いのは彼じゃない。彼が選んだのではなく、業界が選んだのだから。

この言葉は、たぶんもともとは会計方法かなんかから出て来たんじゃないかと思う。 その言葉の意味を乱暴にいえば、「妙なことはするな」ということだ。 会計の世界ではたぶんそれは良いアイディアだ。 でも「最先端」と「会計」はあんまり馴染まない。 だからそんな方法を技術に関する決断に持ち込んだら、 間違った答えを得るようになる。

技術はしばしば、最先端でなければならない。 プログラミング言語では、Erann Gatが示したように、 「業界の最良慣行」はあなたをトップにするのではなく、単に平均にするだけだ。 その決断のせいでより進んだ競争相手の数分の一しか開発ができないのなら、 「最良慣行」は誤った名前だとさえ言える。

ここで、私は2つの情報を述べた。どちらもとても貴重な情報だと思う。 私はそれを自分の経験から知った。 第一に、プログラミング言語はその力に差がある。 第二に、大抵のマネージャーはその事実を無視する。 実はこの二つは、文字通りお金を儲けるレシピになるんだ。 ITAは、このレシピがまさに使われている良い例だ。 ソフトウェアビジネスで勝とうと思ったら、 見付けられるうちで一番難しい問題に目をつけて、 手に入る一番パワフルな言語を使い、 そしてライバル会社の髪のとんがった上司が中庸な技術へと 戻って行くのを待っていればいいのさ。


付録:力

私が言う言語の相対的な力を説明するために、次の問題を考えてみよう。 アキュムレータを生成する関数、すなわち、数nを取り、 「数iを取ってnをiだけ増加させ、その増加した値を返す関数」を返すような関数だ。

(「増加させる」に注意。ただ足すだけではない。 アキュムレータ(累積器)だから累積させなければ)。

Common Lispではこんなふうになるだろう。

(defun foo (n) #'(lambda (i) (incf n i)))

そしてPerl 5ではこうだ

sub foo { my ($n) = @_; sub {$n += shift} }

Perl 5の方がLispのより要素数が多いが、これはPerlではパラメータを自分で 取り出さなければならないからだ。

SmalltalkもLispよりちょっと長くなる

foo: n |s| s := n. ^[:i| s := s+i. ]

レキシカルな変数が使えるが、パラメータには代入できないから、 新しい変数sを作らなくちゃならない。

Javascriptでもこの例はちょっと長くなるが、 それは文と式の区別を持っているために、明示的なreturn文を 使わなければならないからだ。

function foo(n) { return function (i) { return n += i } }

(公正を期するなら、Perlもこの区別を持っているが、 通常の書き方ではreturnを省略できる)

このLisp/Perl/Smalltalk/JavascriptをPythonに移そうとすると ちょっとした制限につきあたる。Pythonには完全なレキシカル変数が無いため、 nの値を保持するデータ構造を作らなければならないからだ。 また、Pythonは関数データ型を持っているが、 その関数が単一の式で表されない場合はリテラル表記できない。 だから名前をつけた関数を作ってそれを返さなければならない。 こんな感じになるだろう。

def foo(n): s = [n] def bar(i): s[0] += i return s[0] return bar

Pythonユーザーはなぜ以下のように書けないか疑問に思って良いと思う。

def foo(n): return lambda i: return n += i あるいは、 def foo(n): lambda i: n += i

そして、たぶんいつの日か、そう書けるようになるだろう。 (ただ、PythonがLispへの最後の道程を進化するのを待てない ユーザには、いつでも使える手段がある。それは…)

OO言語では、一つだけメソッドを持つクラスを定義して メソッドが参照する変数をクラスのフィールドに置き換えれば、 制限はあるものの、 クロージャ(定義されたスコープ内の変数を参照できる関数)を模倣することはできる。 そうすることは、レキシカルスコープを完全にサポートする言語ではコンパイラが やってくれるような作業をプログラマが自分でやるようなものだし、 複数の関数が同一の変数を参照する場合なんかは使えないけれど、 この例のような簡単な場合は十分だ。

Pythonのエキスパートは、こっちの方法の方がPythonらしいということに 同意するんじゃないだろうか。たとえばこう書いたり

def foo(n): class acc: def __init__(self, s): self.s = s def inc(self, i): self.s += i return self.s return acc(n).inc

あるいはこうだ。

class foo: def __init__(self, n): self.n = n def __call__(self, i): self.n += i return self.n

私がこの例を入れたのは、 Python支持者が、私が言語を間違った方法で紹介しているんだと思わないようにだ。 だがどちらの方法も、私にとっては最初の例より複雑に見える。 アキュムレータを保持する場所を作るという同じことをしているだけで、 それが最初の例ではリストの先頭であり、これらの例ではフィールドになっただけだ。 それに特殊な予約フィールド名、特に __call__ はちょっとしたハックのように感じられる。

PerlとPythonの比較では、 Pythonハッカーの方はいつもPythonがPerlよりエレガントに書けると主張しているようだが、 この例を見る限り、言語の力は究極のエレガンスに通じるようだ。 Perlの方が、構文はちょっと醜いが、単純だ (より少ない要素で書ける)。

他の言語ではどうだろう。この話で述べた他の言語--Fortran, C, C++, Java,そしてVisual Basic--では、この問題が解けるかどうかさえ定かではない。 Ken Andersonは、次のコードはJavaでやる限り最も近い解だと述べた

public interface Inttoint { public int call(int i); } public static Inttoint foo(final int n) { return new Inttoint() { int s = n; public int call(int i) { s = s + i; return s; }}; }

これは整数でしか動作しないという点でもとのスペックに届いていない。 Javaハッカーとずいぶんメイルで討論したが、上の方で示したような例と同様に 動作する真に多態なコードをJavaで書くのは、ものすごく不自由と不可能との間だ、 ということのようだ。誰かうまく書けたら是非見せて欲しい。 私は自分でもいろいろやってみたが時間切れだった。

もちろん、他の言語でこの問題を解けないと言ってしまうのは正しくはない。 全ての言語はチューリング等価であるという事実は、厳密に言えば、 いかなるプログラムもどんな言語を使っても書けるということだ。 じゃあどうやって? このようなケースでは、 力の弱い言語でLispインタプリタを書くことで実現できる。

これはジョークのように思えるかもしれないが、 そういうことは大きなプログラミングプロジェクトでは程度の差こそあれ非常に頻繁に 起こっていることだ。だから名前さえついている。「グリーンスパンの第10規則」というものだ。

全ての十分に複雑なCもしくはFortranプログラムは、 後付けの、不完全な仕様とバグを持ち、遅い、Common Lispの半分の実装を含んでいる。

あなたが難しい問題を解こうとしているなら、 問われているのはパワフルな言語を使うか使わないか、ではない。 (a)パワフルな言語を使うか (b)パワフルな言語と等価なインタプリタを書くか (c)自らがパワフルな言語の人間コンパイラとなるか、という選択なのだ。 Pythonの例の中に既にこの選択を見て取ることができる。あの例で我々は、 コンパイラがレキシカルな変数を扱う際に生成するようなコードをシミュレートしていたのだ。

コンパイラがやるべきことを人間がシミュレートするという慣行は ただ広まっているというだけでなく、思考を型にはめる作用がある。 例えば、OOの世界では非常に良く「パターン」というのを耳にするだろう。 この「パターン」は多くの場合、(c)のケース、 すなわち人間コンパイラが実際に動作している証拠なんじゃないかと私は思う。 私が自分のプログラムにパターンを見付けたら、それはどこかがおかしいというサインだ。 プログラムの形は、それが解くべき問題のみを反映すべきだ。 その他の繰り返しがコード中に現れるということは、少なくとも私にとっては、 十分な抽象化を行っていないということを意味する。大抵の場合、それは マクロを書くべきコードを手で拡張して書いているということになる。

原註

関連情報

たくさんの人々がこの講演に対して反響をくれた。 議論に上がったことがらに関して、別にページを設けた: 「技術野郎の復讐---Revenge of the Nerds---」への反響

また、LL1メイリングリストにおいても 長大でしばしば有用な議論に火をつけた。 特に、Anton van Straatenのセマンティックコンプレッションのメイルを参照のこと。

LL1でのいくつかのメイルに触発されて、私は言語の力の問題について 簡潔さは力なりでより深く考えてみた。

アキュムレータ生成のベンチマークに対する、より多くの正規実装は 独自のページにまとめている。


訳註

訳註1

本記事のタイトル、「Revenge of the Nerds」は1984年公開の 米映画のタイトルである。 大学を舞台に、冴えないコンピュータオタク(死語?)達がその技術力で スポーツマンヒーロータイプの連中の鼻を明かす、という話、らしい。 日本公開時の邦題は「ナーズの復讐」であった。 が、「ナーズ」と聞いて "nerd" の複数形と理解し、 かつ英語の "nerd" が内包する意味を想起するのは難しいように思われる。 (邦題を見たとき、一瞬「ナースの復讐? なんでだ?」と思っちゃったし)。

さらに、この記事においてPaul Graham氏が "nerd" で意味したことは、 「ナード」という単語の持つ典型的なイメージ (青白くて対人関係が苦手で、 もっぱらコンピュータを相手にしてて、でも天才的な技術を持ってる) よりは広いもののように思える。 (多分自らが "nerd" に分類されると自負している人はもっと広い意味で その単語をとらえているだろうが、外側から見たイメージはこんなものだろう)。

そこで、Paul Graham氏にこの題名の含むところについて質問してみた。 氏からのe-mailを公開する了承を得たので、参考のために下に訳出しておく。

"Revenge of the Nerds" は映画です。nerd はアメリカの大学では 普通はあまりイケてなくて (コンピューターが「クール」になったおかげで 昔より多少人気は出てきましたが)、だから社会階層の一番下にいます。 この映画は、大学のnerdのグループが(何だったかは忘れましたが) 素晴らしい勝利をおさめて、みんなから称賛されるという内容でした。

英語では、"nerd"という言葉は、流行を追ったり人付き合いをうまくこなすよりも 知的なものごとに興味を惹かれる人を指します。 nerdはいつも本にかじりついていて、 社交的な場面ではおどおどしてへまばかりしています。 ハッカーは大抵nerdです。セールスマンはnerdの対極にあります。 マネージャーはnerdであることもありますが、普通は違います。 ビル・ゲイツはnerdです。ディルバートもnerdです。 アインシュタインもnerdでした。

"Nerd" はもともと、アメリカでは悪い印象がありました。 しかし今ではそれほどでもありません。多分、新しい技術を発明することで 大金持ちになれると皆知ったからでしょう。たくさんのハッカーが 自身をnerdと考えており、そうではないと言われたらむっとするかもしれません。 nerdでないと言われることは、賢くないということを意味しているからです。

私がこの記事を "Revenge of the Nerds" と呼んだのは、 良い技術的なアイディアが、悪くても広くマーケティングされているアイディア (そして愚かなマネージャに好まれるアイディア) に対してゆっくりと、 しかし確実に勝利を収めてゆくということを言いたかったからです。 nerdsの復讐とは、彼らのアイディアが勝つということです。そして ITAの場合(およびViawebの場合も)、保守的な競争相手が歯が立たない 技術を使うことで、彼ら自身も勝利しました。

「オタク」という言葉は、英語ではむしろ "freak" に当たるように 思えます。コンピュータゲームの熱狂的なファンを "computer game freak" と呼んだりします。"nerd" にはそのような意味はありません。

より近い翻訳は、"Revenge of the professors" とか、 "Revenge of the unworldly intellectuals" になるかもしれません。

これを踏まえた上で良い訳語を、と思ったのだが、 訳者の力不足でこれというものに至らなかった。 良い表現を思い付かれた方は教えて頂けたら幸いである。

訳註2

髪のとんがった上司(pointy-haired boss): Dilbertに出て来るキャラクタ。

訳註3

これは実際Lispプログラマの間で良くジョークで言われることだが、 Pythonユーザからの視点として、Paul Prescod氏が反論 PythonとLispの関係についてを書いている。 よくまとまっているので、特にLispプログラマは是非参照されたい。

訳註4

Erann Gat's sad tale: 彼のcomp.lang.lispへの投稿How I lost my faith (long) (和訳)のことだろうか? プロフェッショナルLispプログラマなら、 多かれ少なかれ彼と似たような経験はしてるだろうし、 Remote Agentプロジェクトの査問会の下りは涙なくしては読めまい……。

(2004/3/21) Nao Hirokawaさんより、この「悲しい話」は Lisping at JPL ではないか、との情報を頂いた。「ベストプラクティス」という言葉も出てくる。


[Practical Scheme]