オタクの逆襲

スタートアップを始めたいですか? Y Combinatorから資金提供を受けましょう。


2002年5月

| 「我々はC++プログラマーを狙っていた。彼らの多くをLispに半分まで引きずり込むことに成功した。」

  • ガイ・スティール、Java仕様共同著者

ソフトウェアビジネスには、とんがり頭の学者たちと、もう一つの同様に手ごわい勢力であるとんがり頭のボスたちの間で、絶え間ない闘争がある。とんがり頭のボスが誰であるかは誰もが知っているだろう?テクノロジーの世界にいるほとんどの人は、このカートゥーンのキャラクターを認識しているだけでなく、彼がモデルになった自分の会社にいる実在の人物を知っていると思う。

とんがり頭のボスは、それ自体は一般的だが、めったに一緒に見られない2つの資質を奇跡的に兼ね備えている。(a)彼はテクノロジーについて全く何も知らない、そして(b)彼はそれについて非常に強い意見を持っている。

例えば、あるソフトウェアを書く必要があるとしよう。とんがり頭のボスは、このソフトウェアがどのように機能する必要があるか全く分からず、プログラミング言語の区別もつかないのに、どの言語で書くべきかを知っている。その通りだ。彼はJavaで書くべきだと考えている。

なぜ彼はそう考えるのか?とんがり頭のボスの脳内を覗いてみよう。彼が考えているのは、このようなことだ。Javaは標準だ。プレスでいつも読んでいるから、そうに違いない。標準だから、それを使っても問題にならないだろう。そして、それは常にたくさんのJavaプログラマーがいることを意味する。だから、今私のために働いているプログラマーが辞めても、プログラマーが私のもとから不思議といつも辞めていくように、簡単に彼らを補充できる。

まあ、これはそれほど不合理には聞こえない。しかし、それはすべて一つの暗黙の仮定に基づいており、その仮定は間違っていることが判明する。とんがり頭のボスは、すべてのプログラミング言語はほとんど同じであると信じている。もしそれが本当なら、彼は的を射ているだろう。もし言語がすべて同じなら、もちろん、他の誰もが使っている言語を使えばいい。

しかし、すべての言語が同じではない。そして、私はそれらの違いに触れることなく、それをあなたに証明できると思う。もし1992年にとんがり頭のボスに、ソフトウェアはどの言語で書かれるべきかと尋ねたら、彼は今日と同じくらいためらいなく答えただろう。ソフトウェアはC++で書かれるべきだと。しかし、もし言語がすべて同じなら、なぜとんがり頭のボスの意見が変わる必要があるのか?実際、なぜJavaの開発者たちは、わざわざ新しい言語を作る必要があったのか?

おそらく、新しい言語を作るなら、それは既存のものよりも何らかの点で優れていると考えているからだろう。そして実際、ゴスリングは最初のJavaホワイトペーパーで、JavaがC++のいくつかの問題を解決するために設計されたことを明確にしている。だから、これだ:言語はすべて同じではない。とんがり頭のボスの脳を通ってJavaへ、そしてJavaの歴史を遡ってその起源へとたどっていくと、最初に仮定したことと矛盾する考えに行き着く。

では、どちらが正しいのか?ジェームズ・ゴスリングか、それともとんがり頭のボスか?驚くことではないが、ゴスリングが正しい。ある言語は、特定の問題に対して、他の言語よりも_優れている_。そして、それはいくつかの興味深い疑問を提起する。Javaは、特定の問題に対して、C++よりも優れているように設計された。どんな問題か?Javaが優れているのはいつで、C++が優れているのはいつか?それらのどちらよりも他の言語が優れている状況はあるのか?

この問題を考え始めると、本当に厄介な問題に直面することになる。とんがり頭のボスがその複雑さ全体を考えなければならないとしたら、彼の脳は爆発するだろう。彼がすべての言語を同じだと考えている限り、彼がしなければならないのは、最も勢いがあるように見えるものを選ぶことだけだ。そして、それはテクノロジーよりも流行の問題であるため、彼でさえおそらく正しい答えを出すことができる。しかし、言語が異なる場合、彼は突然、彼が何も知らない2つのことの間で最適なバランスを見つけようと、2つの連立方程式を同時に解かなければならなくなる。それは、彼が解決する必要がある問題に対する20ほどの主要言語の相対的な適合性と、それぞれの言語のプログラマーやライブラリなどを見つける可能性だ。もしそれがドアの向こうにあるものなら、とんがり頭のボスがそれを開きたがらないのも当然だ。

すべてのプログラミング言語が同じであると信じることの欠点は、それが真実ではないことだ。しかし、利点は、それがあなたの人生をはるかに単純にすることだ。そして、それがこの考えがこれほど広まっている主な理由だと思う。それは_快適な_考えなのだ。

Javaはクールで新しいプログラミング言語だから、かなり良いに違いないと私たちは知っている。本当にそうだろうか?プログラミング言語の世界を遠くから見ると、Javaが最新のもののように見える。(十分遠くから見ると、Sunが費用を払った大きな点滅する広告看板しか見えない。)しかし、この世界を間近で見ると、クールさには度合いがあることがわかる。ハッカーのサブカルチャーの中では、Javaよりもずっとクールだと考えられているPerlという別の言語がある。例えば、SlashdotはPerlで生成されている。彼らがJava Server Pagesを使っているとは思えない。しかし、Pythonという別の新しい言語があり、そのユーザーはPerlを見下す傾向があり、さらに控えているものもある。

これらの言語をJava、Perl、Pythonの順に見ると、興味深いパターンに気づく。少なくとも、あなたがLispハッカーであれば、このパターンに気づくだろう。それぞれが段階的にLispに似てきている。Pythonは、多くのLispハッカーが間違いだと考える機能さえもコピーしている。単純なLispプログラムをPythonに1行ずつ翻訳できる。2002年であり、プログラミング言語はようやく1958年に追いついたところだ。

数学に追いつく

私が言いたいのは、Lispは1958年にジョン・マッカーシーによって初めて発見され、人気のあるプログラミング言語は、彼が当時開発したアイデアにようやく追いついているところだということだ。

さて、どうしてそれが本当だと言えるのか?コンピュータ技術は非常に急速に変化するものではないのか?つまり、1958年には、コンピュータは冷蔵庫サイズの巨大なもので、腕時計程度の処理能力しかなかった。そんなに古い技術が、最新の開発よりも優れているどころか、どうして関連性があると言えるのか?

その理由を教えよう。それはLispが、少なくとも今日私たちが意味するような意味でのプログラミング言語として、実際には設計されていなかったからだ。私たちがプログラミング言語と呼ぶのは、コンピュータに何をすべきかを伝えるために使うものだ。マッカーシーは最終的にこの意味でのプログラミング言語を開発するつもりだったが、私たちが実際に手にしたLispは、彼が理論的な演習として行った別のものに基づいていた――チューリングマシンに代わる、より便利なものを定義しようとする試みだった。マッカーシーが後に語ったように、

Lispがチューリングマシンよりも優れていることを示すもう一つの方法は、普遍的なLisp関数を書き、それが普遍的なチューリングマシンの記述よりも簡潔で理解しやすいことを示すことだった。これがLispの式を計算するLisp関数evalだった…。_eval_を書くには、Lisp関数をLispデータとして表現する記法を発明する必要があり、そのような記法は、実際にLispプログラムを表現するために使われるとは全く考えずに、論文の目的のために考案された。

次に起こったのは、1958年後半のある時、マッカーシーの大学院生の一人であるスティーブ・ラッセルが、この_eval_の定義を見て、もしそれを機械語に翻訳すれば、Lispインタープリタになるだろうと気づいたことだ。

これは当時、大きな驚きだった。マッカーシーが後にインタビューでそれについて語ったのは次の通りだ。

スティーブ・ラッセルが言った、「見てください、なぜ私がこの_eval_をプログラムしないのですか…」と。私は彼に言った、「ほほう、君は理論と実践を混同している。この_eval_は読むためのものであって、計算するためのものではない。」しかし、彼はそれを実行した。つまり、彼は私の論文の_eval_を[IBM] 704の機械語にコンパイルし、バグを修正し、そしてこれをLispインタープリタとして宣伝した。それは確かにそうだった。こうして、その時点でLispは本質的に今日と同じ形になった…。

突然、数週間のうちに、マッカーシーは彼の理論的な演習が実際のプログラミング言語――そして彼が意図していたよりも強力なもの――に変貌したのを発見したのだ。

だから、この1950年代の言語が時代遅れではないことの短い説明は、それがテクノロジーではなく数学であり、数学は古くならないからだ。Lispを比較すべきは1950年代のハードウェアではなく、例えば1960年に発見され、今でも最速の汎用ソートであるクイックソートアルゴリズムだ。

1950年代から今も生き残っている言語がもう一つある。Fortranだ。そして、それは言語設計に対する正反対のアプローチを代表している。Lispは予期せずプログラミング言語に転用された理論の一部だった。Fortranは意図的にプログラミング言語として開発されたが、今では非常に低レベルだと考えられるものだった。

1956年に開発された言語であるFortran Iは、現在のFortranとは全く異なるものだった。Fortran Iは、ほとんど数学付きのアセンブリ言語だった。ある意味では、より最近のアセンブリ言語よりも力が劣っていた。例えば、サブルーチンはなく、分岐しかなかった。現在のFortranは、Fortran IよりもLispに近いと言えるだろう。

LispとFortranは、2つの異なる進化の木の幹だった。一方は数学に根ざし、もう一方はマシンアーキテクチャに根ざしている。これら2つの木は、それ以来ずっと収束している。Lispは強力な状態で始まり、次の20年間で高速になった。いわゆる主流言語は高速な状態で始まり、次の40年間で徐々に強力になり、今では最も進んだものはLispにかなり近い。近いが、まだいくつかのものが欠けている…。

Lispを異ならせたもの

Lispが最初に開発されたとき、それは9つの新しいアイデアを具現化していた。これらのアイデアのいくつかは今では当たり前だと考えられているが、他はより高度な言語にしか見られず、2つはLispに固有のものである。主流に採用された順に、9つのアイデアは次の通りだ。

  1. 条件分岐。条件分岐とは、if-then-elseの構造である。今では当たり前だと考えられているが、Fortran Iにはそれがなかった。それは、基盤となる機械命令に密接に基づいた条件付きgotoしかなかった。

  2. 関数型。Lispでは、関数は整数や文字列と同じデータ型である。それらはリテラル表現を持ち、変数に格納でき、引数として渡すことができ、などなど。

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

  4. 動的型付け。Lispでは、すべての変数は実質的にポインタである。型を持つのは値であり、変数ではない。そして、変数を割り当てる、またはバインドすることは、ポインタが指すものではなく、ポインタをコピーすることを意味する。

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

  6. 式で構成されるプログラム。Lispプログラムは式のツリーであり、それぞれが値を返す。これは、式と文を区別するFortranやその後のほとんどの言語とは対照的だ。

Fortran Iでは、文をネストできなかったため、この区別があるのは自然だった。そして、数学が機能するためには式が必要だったが、他のものに値を返させる意味はなかった。なぜなら、それを待っているものが何もなかったからだ。

この制限はブロック構造言語の登場とともに解消されたが、その時には手遅れだった。式と文の区別は定着していた。それはFortranからAlgolへ、そして両者の子孫へと広がった。

  1. シンボル型。シンボルは、ハッシュテーブルに格納された文字列への実質的なポインタである。そのため、各文字を比較する代わりに、ポインタを比較することで等価性をテストできる。

  2. シンボルと定数のツリーを用いたコードの記法。

  3. 言語全体が常にそこにある。読み込み時、コンパイル時、実行時の間に本当の区別がない。読み込み中にコードをコンパイルまたは実行でき、コンパイル中にコードを読み込みまたは実行でき、実行時にコードを読み込みまたはコンパイルできる。

読み込み時にコードを実行することで、ユーザーはLispの構文を再プログラムできる。コンパイル時にコードを実行することはマクロの基礎である。実行時にコンパイルすることはEmacsのようなプログラムにおける拡張言語としてのLispの使用の基礎であり、実行時に読み込むことでプログラムがs-expressionを使って通信することを可能にする。これは最近XMLとして再発明されたアイデアだ。

Lispが最初に登場したとき、これらのアイデアは、1950年代後半に利用可能だったハードウェアに大きく左右されていた通常のプログラミング実践とはかけ離れたものだった。時が経つにつれて、一連の人気言語に具現化されたデフォルト言語は、徐々にLispに向かって進化してきた。アイデア1~5は今では広く普及している。6番は主流に現れ始めている。Pythonには7の形式があるが、それに対する構文はないようだ。

8番については、これが最も興味深いかもしれない。アイデア8と9は、スティーブ・ラッセルがマッカーシーが意図しなかったものを実装したため、偶然Lispの一部となった。しかし、これらのアイデアがLispの奇妙な外観と最も特徴的な機能の両方の原因であることが判明している。Lispが奇妙に見えるのは、奇妙な構文を持っているからというよりは、構文がないからだ。他の言語がパースされるときに舞台裏で構築されるパースツリーで直接プログラムを表現する。そして、これらのツリーはリストでできており、リストはLispのデータ構造である。

言語を自身のデータ構造で表現することは、非常に強力な機能であることが判明している。アイデア8と9を合わせると、プログラムを書くプログラムを書くことができるということだ。それは奇妙なアイデアに聞こえるかもしれないが、Lispでは日常的なことだ。最も一般的な方法は、_マクロ_と呼ばれるものを使うことだ。

「マクロ」という用語は、Lispでは他の言語が意味するものとは異なる。Lispマクロは、略語から新しい言語のコンパイラまで、何でもありうる。もしLispを本当に理解したい、あるいは単にプログラミングの視野を広げたいなら、マクロについてもっと学ぶべきだろう。

マクロ(Lispの意味で)は、私の知る限り、今でもLispに固有のものだ。これは、マクロを持つためには、おそらくLispのように奇妙な言語にする必要があるからという部分もある。また、もしその最後の力の増分を追加すると、新しい言語を発明したとは主張できず、Lispの新しい方言を発明したとしか言えなくなるからかもしれない。

これはほとんど冗談として言及しているが、全くその通りだ。もしあなたがcar、cdr、cons、quote、cond、atom、eqを持ち、リストとして表現された関数の記法を持つ言語を定義するなら、Lispの残りのすべてをそこから構築できる。それが実際、Lispの決定的な特質だ。マッカーシーがLispに現在の形を与えたのは、これを可能にするためだったのだ。

言語が重要となる場所

では、Lispが主流言語が漸近的に近づいている一種の限界を表していると仮定しよう――それは、実際にLispを使ってソフトウェアを書くべきだという意味なのだろうか?力の劣る言語を使うことで、どれくらい失うのだろうか?時には、イノベーションの最先端にいない方が賢明ではないか?そして、人気はある程度、それ自体の正当化ではないか?例えば、とんがり頭のボスがプログラマーを簡単に雇える言語を使いたがるのは、正しくないのだろうか?

もちろん、プログラミング言語の選択がそれほど重要ではないプロジェクトもある。原則として、アプリケーションの要求度が高いほど、強力な言語を使うことでより大きなレバレッジが得られる。しかし、多くのプロジェクトは全く要求度が高くない。ほとんどのプログラミングは、小さな接着プログラムを書くことで構成されており、小さな接着プログラムには、すでに慣れていて、必要なことのための良いライブラリがあるどんな言語でも使える。あるWindowsアプリから別のアプリにデータを供給する必要があるだけなら、もちろんVisual Basicを使えばいい。

小さな接着プログラムはLispでも書ける(私はデスクトップ計算機として使っている)が、Lispのような言語の最大の利点は、スペクトルの反対側、つまり激しい競争の中で難しい問題を解決するために洗練されたプログラムを書く必要がある場合だ。良い例は、ITA SoftwareがOrbitzにライセンス供与している航空運賃検索プログラムだ。彼らは、TravelocityとExpediaという2つの巨大で確立された競合他社にすでに支配されていた市場に参入し、技術的に彼らを屈辱させたようだ。

ITAのアプリケーションの中核は、20万行のCommon Lispプログラムであり、競合他社よりも桁違いに多くの可能性を検索する。競合他社は、どうやらまだメインフレーム時代のプログラミング技術を使っているようだ。(ITAも、ある意味ではメインフレーム時代のプログラミング言語を使っているのだが。)私はITAのコードを見たことはないが、彼らのトップハッカーの一人によると、彼らは多くのマクロを使っているそうで、それを聞いても驚かない。

求心力

珍しい技術を使うことにコストがないと言っているわけではない。とんがり頭のボスがこれを心配するのは、全くの間違いではない。しかし、彼はリスクを理解していないため、それらを誇張する傾向がある。

あまり一般的でない言語を使うことから生じうる3つの問題を考えることができる。あなたのプログラムが他の言語で書かれたプログラムとうまく連携しないかもしれない。利用できるライブラリが少ないかもしれない。そして、プログラマーを雇うのに苦労するかもしれない。

これらの問題はそれぞれどれくらい重要なのか?最初の問題の重要性は、システム全体を制御しているかどうかによって異なる。もしバグだらけの閉鎖的なオペレーティングシステム(名前は挙げない)上で、リモートユーザーのマシンで実行する必要があるソフトウェアを書いているなら、OSと同じ言語でアプリケーションを書く利点があるかもしれない。しかし、ITAがそうしているように、システム全体を制御し、すべての部分のソースコードを持っているなら、好きな言語を使うことができる。もし互換性の問題が発生しても、自分で修正できる。

サーバーベースのアプリケーションでは、最も高度な技術を使うことができる。そして、これがジョナサン・エリクソンが「プログラミング言語のルネサンス」と呼ぶものの主な原因だと思う。だからこそ、PerlやPythonのような新しい言語について耳にするのだ。これらの言語について耳にするのは、人々がそれらを使ってWindowsアプリを書いているからではなく、サーバーで使っているからだ。そして、ソフトウェアがデスクトップからサーバーへと移行するにつれて(Microsoftでさえ諦めている未来だ)、中庸な技術を使うことへのプレッシャーはますます少なくなるだろう。

ライブラリについては、その重要性もアプリケーションによって異なる。要求度の低い問題では、ライブラリの利用可能性が言語本来の力を上回ることがある。損益分岐点はどこか?正確に言うのは難しいが、それがどこであれ、アプリケーションと呼ぶようなものには及ばない。もし会社がソフトウェアビジネスに属していると考えており、彼らが製品となるアプリケーションを書いているなら、おそらく数人のハッカーが関わり、書くのに少なくとも6ヶ月はかかるだろう。その規模のプロジェクトでは、強力な言語が既存のライブラリの利便性を上回り始めるだろう。

とんがり頭のボスの3番目の心配、プログラマーを雇うことの難しさについては、私は誤解を招くものだと思う。結局のところ、何人のハッカーを雇う必要があるのか?ソフトウェアは10人未満のチームで開発するのが最適であることは、今や誰もが知っているはずだ。そして、誰もが聞いたことのある言語であれば、その規模でハッカーを雇うのに苦労することはないはずだ。もし10人のLispハッカーを見つけられないなら、あなたの会社はソフトウェア開発に適さない都市に拠点を置いている可能性が高い。

実際、より強力な言語を選択することは、必要なチームの規模を減らす可能性が高い。なぜなら、(a)より強力な言語を使えば、おそらくそれほど多くのハッカーを必要としないだろうし、(b)より高度な言語で作業するハッカーは、より賢い傾向があるからだ。

「標準」技術と認識されているものを使うように、多くのプレッシャーを受けることはないとは言わない。Viaweb(現在のYahoo Store)では、Lispを使ったことでVCや潜在的な買収候補者の間で眉をひそめさせた。しかし、私たちはまた、「産業用」サーバーであるSunのようなものを使う代わりに汎用のIntel製ボックスをサーバーとして使ったこと、Windows NTのような真の商用OSの代わりに当時無名だったオープンソースのUnix派生版であるFreeBSDを使ったこと、今では誰も覚えていないSETという電子商取引の標準を無視したことなどでも眉をひそめさせた。

スーツを着た人々に技術的な決定をさせてはならない。Lispを使ったことで、一部の潜在的な買収候補者を少し警戒させたか?確かにそうだが、もしLispを使っていなかったら、彼らが私たちを買収したいと思うようなソフトウェアを書くことはできなかっただろう。彼らにとって異常に見えたことは、実際には原因と結果だったのだ。

スタートアップを始めるなら、VCや潜在的な買収候補者を喜ばせるために製品を設計してはならない。ユーザーを喜ばせるように製品を設計するのだ。ユーザーを獲得すれば、他のすべてはついてくる。そして、もし獲得できなければ、あなたの技術選択がいかに安心できるほど正統的であったかなど、誰も気にしないだろう。

平均であることのコスト

力の劣る言語を使うことで、どれくらい失うのだろうか?実際、それに関するデータがいくつか存在する。

便利な力の尺度は、おそらくコードサイズだろう。高水準言語の目的は、より大きな抽象化――いわば、より大きなレンガ――を提供することであり、それによって特定のサイズの壁を構築するのに必要なレンガの数が少なくなる。したがって、言語が強力であるほど、プログラムは短くなる(もちろん、単に文字数だけでなく、異なる要素の数で)。

より強力な言語は、どのようにしてより短いプログラムを書くことを可能にするのか?もし言語が許すなら、ボトムアッププログラミングと呼ばれる技術を使うことができる。ベース言語でアプリケーションを単に書くのではなく、ベース言語の上に、あなたのようなプログラムを書くための言語を構築し、その言語でプログラムを書くのだ。結合されたコードは、プログラム全体をベース言語で書いた場合よりもはるかに短くなる可能性がある――実際、ほとんどの圧縮アルゴリズムはこのように機能する。ボトムアッププログラムは修正も容易になるはずだ。なぜなら、多くの場合、言語レイヤーは全く変更する必要がないからだ。

コードサイズは重要だ。なぜなら、プログラムを書くのにかかる時間は、その長さにほとんど依存するからだ。もしあなたのプログラムが別の言語で3倍の長さになるなら、書くのに3倍の時間がかかるだろう――そして、より多くの人を雇ってもこれを回避することはできない。なぜなら、ある程度の規模を超えると、新規採用は実際には純損失になるからだ。フレデリック・ブルックスは彼の有名な著書『人月の神話』でこの現象を記述しており、私がこれまで見てきたことはすべて、彼の言ったことを裏付ける傾向がある。

では、Lispでプログラムを書くと、どれくらい短くなるのだろうか?例えば、Lisp対Cの場合、私が聞いたほとんどの数字は7~10倍程度だった。しかし、『New Architect』誌のITAに関する最近の記事では、「Lispの1行はCの20行を置き換えることができる」と述べられており、この記事はITAの社長からの引用でいっぱいだったため、この数字はITAから得られたものだと推測する。もしそうなら、私たちはそれを信用できる。ITAのソフトウェアにはLispだけでなく多くのCやC++も含まれているため、彼らは経験から語っているのだ。

私の推測では、これらの倍率は一定ではない。より難しい問題に直面するときや、より賢いプログラマーがいるときに増加すると思う。本当に優秀なハッカーは、より良いツールからより多くを引き出すことができる。

いずれにせよ、曲線上のデータ点として、もしあなたがITAと競合し、ソフトウェアをCで書くことを選択した場合、彼らはあなたよりも20倍速くソフトウェアを開発できるだろう。あなたが新機能に1年を費やした場合、彼らは3週間足らずでそれを複製できる。一方、彼らが何か新しいものを開発するのにわずか3ヶ月を費やした場合、あなたがそれを持つまでに_5年_かかるだろう。

そして、ご存知だろうか?それは最良のシナリオだ。コードサイズの比率について話すとき、あなたは暗黙のうちに、弱い言語で実際にプログラムを書けることを仮定している。しかし実際には、プログラマーができることには限界がある。あまりにも低レベルな言語で難しい問題を解決しようとすると、一度に頭に入れておくには多すぎるという点に達する。

だから、ITAの架空の競合他社がITAがLispで3ヶ月で書けるものを複製するのに5年かかると私が言うとき、それは何も問題が起こらなければ5年という意味だ。実際、ほとんどの会社でのやり方では、5年かかるような開発プロジェクトは、結局、決して完成しない可能性が高い。

これは極端なケースだと認める。ITAのハッカーたちは異常に賢いようだ。そしてCはかなり低レベルな言語だ。しかし、競争の激しい市場では、2対1または3対1の差でさえ、あなたが常に遅れをとることを保証するのに十分だろう。

レシピ

これは、とんがり頭のボスが考えたくもない種類の可能性だ。だから、彼らのほとんどは考えない。なぜなら、結局のところ、とんがり頭のボスは、自分の会社の尻が蹴られても気にしない。誰もそれが彼のせいだと証明できない限りは。彼個人にとって最も安全な計画は、群れの中心に密着することだ。

大組織内では、このアプローチを説明するために「業界のベストプラクティス」というフレーズが使われる。その目的は、とんがり頭のボスを責任から守ることだ。もし彼が「業界のベストプラクティス」であるものを選び、会社が負けても、彼は非難されない。彼が選んだのではなく、業界が選んだのだ。

私はこの用語が元々、会計方法などを説明するために使われたと信じている。その意味は、おおよそ_変なことはするな_ということだ。そして、会計においては、それはおそらく良いアイデアだろう。「最先端」と「会計」という言葉は、一緒に聞くとあまり良い響きではない。しかし、この基準を技術に関する決定に持ち込むと、間違った答えが出始める。

テクノロジーはしばしば_最先端であるべきだ_。プログラミング言語においては、エラン・ガットが指摘したように、「業界のベストプラクティス」が実際にあなたにもたらすのは、最高のものではなく、単なる平均である。決定によって、より積極的な競合他社の何分の一かの速度でソフトウェアを開発することになる場合、「ベストプラクティス」は誤称だ。

だから、ここに非常に価値があると思う2つの情報がある。実際、私自身の経験から知っている。1つ目は、言語は力において異なるということ。2つ目は、ほとんどのマネージャーは意図的にこれを無視するということだ。これら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では手動でパラメータを抽出する必要があるため、Lisp版よりも要素が多い。

Smalltalkでは、コードはLispよりもわずかに長い。foo: n |s| s := n. ^[:i| s := s+i. ] なぜなら、一般的に字句変数(レキシカル変数)は機能するものの、パラメータへの代入ができないため、新しい変数sを作成する必要があるからだ。

Javascriptでは、例はやはりわずかに長い。Javascriptは文と式の区別を保持しているため、値を返すために明示的なreturn文が必要だ。function foo(n) { return function (i) { return n += i } } (公平を期すために言えば、Perlもこの区別を保持しているが、returnを省略できるようにすることでPerlらしいやり方で対処している。)

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言語では、クロージャ(囲むスコープで定義された変数を参照する関数)を、1つのメソッドと、囲むスコープからの各変数を置き換えるためのフィールドを持つクラスを定義することで、限定的にシミュレートできる。これにより、プログラマーが、字句スコープを完全にサポートする言語ではコンパイラが行うようなコード分析を行うことになる。そして、複数の関数が同じ変数を参照する場合、機能しないが、このような単純なケースでは十分だ。

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――では、この問題を実際に解決できるかどうかは不明だ。ケン・アンダーソンは、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ハッカーとの多くのメールのやり取りの後、私は、前述の例のように動作する、適切にポリモーフィックなバージョンを書くことは、非常に扱いにくいと不可能の間くらいだと言えるだろう。もし誰か書ける人がいたら、ぜひ見てみたいが、私個人は時間切れになった。

もちろん、他の言語でこの問題を解決できないというのは文字通りの意味ではない。これらの言語がすべてチューリング等価であるという事実は、厳密に言えば、それらのいずれの言語でもどんなプログラムでも書けることを意味する。では、どうやってそれを行うのか?極限の場合、力の劣る言語でLispインタープリタを書くことによってだ。

それは冗談のように聞こえるが、大規模なプログラミングプロジェクトでは、様々な程度でそれが頻繁に起こるため、その現象には名前がある。グリーンスパンの第十法則だ。

どんなに複雑なCまたはFortranプログラムも、アドホックで非公式に指定された、バグだらけで遅いCommon Lispの半分を実装している。

難しい問題を解決しようとするとき、問題は十分強力な言語を使うかどうかではなく、(a)強力な言語を使うか、(b)事実上のインタープリタを書くか、(c)自分自身がそのための人間コンパイラになるか、だ。Pythonの例では、すでにこれが起こり始めている。そこでは、字句変数を実装するためにコンパイラが生成するコードを実質的にシミュレートしているのだ。

この実践は一般的であるだけでなく、制度化されている。例えば、OOの世界では「パターン」についてよく耳にする。これらのパターンが、場合によっては(c)人間コンパイラが機能している証拠ではないかと私は思う。私のプログラムでパターンを見つけるとき、私はそれを問題の兆候だと考える。プログラムの形は、解決する必要がある問題のみを反映すべきだ。コード内の他の規則性は、少なくとも私にとっては、十分強力でない抽象化を使っている――しばしば、書く必要があるマクロの展開を手動で生成している――兆候なのだ。

注釈

  • IBM 704 CPUは冷蔵庫くらいの大きさだったが、はるかに重かった。CPUは3150ポンドの重さがあり、4KのRAMは別の箱に入っており、さらに4000ポンドの重さがあった。家庭用冷蔵庫で最大級のSub-Zero 690は656ポンドの重さがある。

  • スティーブ・ラッセルは、1962年に最初の(デジタル)コンピュータゲームであるSpacewarも書いた。

  • とんがり頭のボスを騙してLispでソフトウェアを書かせるには、XMLだと言ってみるのも手だ。

  • 他のLisp方言でのアキュムレータジェネレータは次の通りだ。 Scheme: (define (foo n) (lambda (i) (set! n (+ n i)) n)) Goo: (df foo (n) (op incf n _))) Arc: (def foo (n) [++ n _])

  • JPLでの「業界のベストプラクティス」に関するエラン・ガットの悲しい話は、この一般的に誤用されているフレーズについて言及するきっかけとなった。

  • ピーター・ノーヴィグは、『デザインパターン』にある23のパターンのうち16がLispでは「見えないか、より単純」であることを発見した。

  • 様々な言語に関する私の質問に答え、またこの草稿を読んでくださった多くの方々、Ken Anderson、Trevor Blackwell、Erann Gat、Dan Giffin、Sarah Harlin、Jeremy Hylton、Robert Morris、Peter Norvig、Guy Steele、そしてAnton van Straatenに感謝します。表明されたいかなる意見についても、彼らに責任はありません。

関連:

この講演には多くの人々が反応してくれたので、彼らが提起した問題に対処するための追加ページを設けました:Re: オタクの逆襲

また、LL1メーリングリストで広範かつしばしば有益な議論が巻き起こりました。特にアントン・ファン・ストラーテンによる意味的圧縮に関するメールをご覧ください。

LL1でのいくつかのメールは、簡潔さは力なりという言語の力に関する主題をより深く掘り下げるきっかけとなりました。

アキュムレータジェネレータのベンチマークのより大規模な標準実装のセットは、独自のページにまとめられています。

日本語訳スペイン語訳中国語訳