復讐のオタクたち
Original2002年5月
「私たちはC++プログラマーを狙っていました。私たちは彼らの多くをLispに半分引きずり込むことに成功しました。」
- ガイ・スティール、Java仕様の共著者
ソフトウェア業界では、尖った頭の学者たちと、もう一つの同じくらい手強い力、尖った髪の上司との間で継続的な闘争があります。誰もが尖った髪の上司が誰かを知っていますよね?技術の世界にいるほとんどの人は、この漫画キャラクターを認識するだけでなく、彼がモデルにしている実際の人物を自分の会社で知っています。
尖った髪の上司は、技術について何も知らないという二つの特性を奇跡的に組み合わせていますが、これらは単独では一般的ですが、一緒に見られることはめったにありません。 (a) 彼は技術について全く知識がなく、 (b) それについて非常に強い意見を持っています。
例えば、ソフトウェアを書く必要があるとしましょう。尖った髪の上司は、このソフトウェアがどのように機能する必要があるのか全く理解しておらず、プログラミング言語を区別することもできませんが、どの言語で書くべきかは知っています。 その通りです。彼はあなたがJavaで書くべきだと思っています。
なぜ彼はそう思うのでしょうか?尖った髪の上司の脳の中を覗いてみましょう。彼が考えていることはこんな感じです。Javaは標準です。私はそれが標準であるに違いないと思います。なぜなら、私は常にメディアでそれについて読んでいるからです。 それが標準である限り、私はそれを使っても問題ありません。 そして、それはまた、常に多くのJavaプログラマーがいることを意味します。だから、今私のために働いているプログラマーが辞めても、プログラマーが私のために働いているのは神秘的にいつもそうなので、私は簡単に彼らを置き換えることができます。
まあ、これはそれほど不合理には聞こえません。しかし、これはすべて一つの暗黙の前提に基づいており、その前提は間違っていることが判明します。尖った髪の上司は、すべてのプログラミング言語がほぼ同等であると信じています。 もしそれが真実であれば、彼は的を射ているでしょう。もし言語がすべて同等であれば、もちろん、他の誰もが使っている言語を使えばいいのです。
しかし、すべての言語が同等ではなく、私はこれをあなたに証明できると思います。もし1992年に尖った髪の上司にソフトウェアを書くべき言語を尋ねたら、彼は今日と同じくらいためらうことなく答えたでしょう。ソフトウェアはC++で書くべきです。しかし、もし言語がすべて同等であれば、なぜ尖った髪の上司の意見が変わるべきなのでしょうか?実際、なぜJavaの開発者たちは新しい言語を作る必要があったのでしょうか?
おそらく、新しい言語を作るのは、すでにあったものよりも何らかの点で優れていると考えているからです。実際、ゴスリングは最初のJavaホワイトペーパーで、JavaはC++のいくつかの問題を修正するために設計されたことを明確にしています。 だから、あなたはそれを持っています:言語はすべて同等ではありません。 尖った髪の上司の脳を通ってJavaに至り、次にJavaの歴史をたどってその起源に戻ると、あなたは最初に持っていた前提に矛盾するアイデアを持つことになります。
さて、誰が正しいのでしょうか?ジェームズ・ゴスリングか、尖った髪の上司か? 驚くことではありませんが、ゴスリングが正しいです。特定の問題に対して、いくつかの言語は他の言語よりも優れています。そして、これはいくつかの興味深い質問を引き起こします。Javaは特定の問題に対してC++よりも優れているように設計されました。どの問題ですか?Javaが優れているのはいつで、C++が優れているのはいつですか?他の言語がどちらよりも優れている状況はありますか?
この質問を考え始めると、あなたは本当に厄介な問題を開けてしまったことになります。尖った髪の上司が問題の全体的な複雑さについて考えなければならない場合、彼の脳は爆発するでしょう。彼がすべての言語を同等だと考えている限り、彼がしなければならないのは、最も勢いのあるものを選ぶことだけです。そして、それは技術よりもファッションの問題であるため、彼でもおそらく正しい答えを得ることができるでしょう。 しかし、もし言語が異なるのであれば、彼は突然、彼が何も知らない二つのことの間で最適なバランスを見つけようとする二つの同時方程式を解かなければならなくなります:彼が解決する必要がある問題に対する約20の主要な言語の相対的な適合性と、それぞれのプログラマー、ライブラリなどを見つける確率。 もしそれがドアの向こう側にあるものであれば、尖った髪の上司がそれを開けたくないのは驚くことではありません。
すべてのプログラミング言語が同等であると信じることの欠点は、それが真実ではないことです。しかし、その利点は、あなたの生活をはるかに簡単にすることです。 そして、私はそれがこの考えが広まっている主な理由だと思います。 それは快適な考えです。
私たちはJavaがかなり良いに違いないことを知っています。なぜなら、それはクールで新しいプログラミング言語だからです。あるいはそうでしょうか?プログラミング言語の世界を遠くから見ると、Javaが最新のものであるように見えます。(遠くから見ると、あなたが見ることができるのは、サンが支払った大きな点滅する看板だけです。) しかし、この世界を近くで見ると、クールさの度合いがあることがわかります。ハッカーのサブカルチャーの中には、Javaよりもはるかにクールだと見なされているPerlという別の言語があります。例えば、SlashdotはPerlによって生成されています。私は、あの連中がJavaサーバーページを使っているとは思いません。しかし、Pythonという別の新しい言語があり、そのユーザーはPerlを見下す傾向があり、さらに待機しています。
これらの言語を順に見ると、Java、Perl、Pythonの間に興味深いパターンが見られます。少なくとも、あなたがLispハッカーであれば、このパターンに気づくでしょう。それぞれはLispにますます似ています。Pythonは、多くのLispハッカーが間違いだと考える機能さえもコピーしています。 単純なLispプログラムをPythonに行単位で翻訳することができます。 2002年です。プログラミング言語は1958年にほぼ追いついています。
数学に追いつく
私が言いたいのは、Lispは1958年にジョン・マッカーシーによって最初に発見され、人気のあるプログラミング言語は今や彼が当時開発したアイデアに追いついているということです。
さて、どうしてそれが真実であり得るのでしょうか?コンピュータ技術は非常に急速に変化するものではないのでしょうか?つまり、1958年には、コンピュータは冷蔵庫サイズの巨大なもので、腕時計の処理能力しかありませんでした。どうしてそんな古い技術が関連性を持つことができるのでしょうか?最新の開発に優れているどころか。
その理由をお教えしましょう。それは、Lispが本当にプログラミング言語として設計されたわけではないからです。少なくとも、私たちが今日意味する意味でのプログラミング言語ではありません。私たちがプログラミング言語で意味するのは、コンピュータに何をするかを指示するために使用するものです。マッカーシーは最終的にこの意味でのプログラミング言語を開発するつもりでしたが、実際に私たちが手に入れたLispは、彼が理論的な演習として行った別のものに基づいていました。チューリングマシンのより便利な代替手段を定義する試みです。 マッカーシーは後にこう言いました。
Lispがチューリングマシンよりも優れていることを示す別の方法は、普遍的なLisp関数を書き、それが普遍的なチューリングマシンの説明よりも簡潔で理解しやすいことを示すことでした。 これはLisp関数eval...であり、Lisp式の値を計算します.... evalを書くには、Lisp関数をLispデータとして表す表記法を発明する必要があり、そのような表記法は、実際にLispプログラムを表現するために使用されることを考慮せずに論文の目的のために考案されました。
次に起こったのは、1958年の終わり頃、スティーブ・ラッセル、マッカーシーの大学院生の一人が、このevalの定義を見て、それを機械語に翻訳すれば、Lispインタープリターが得られることに気づいたことです。
これは当時大きな驚きでした。 マッカーシーが後にインタビューでこう言ったことがあります。
スティーブ・ラッセルは言いました。「見てください、なぜ私はこのevalをプログラムしないのですか?」私は彼に言いました。「おお、あなたは理論と実践を混同しています。このevalは計算のためではなく、読むためのものです。」しかし、彼はそれを進めて実行しました。つまり、彼は私の論文のevalを[IBM] 704機械コードにコンパイルし、バグを修正し、これをLispインタープリターとして宣伝しました。確かにそれはそうでした。したがって、その時点でLispは本質的に今日の形を持っていました....
突然、数週間のうちに、マッカーシーは彼の理論的な演習が実際のプログラミング言語に変わり、彼が意図したよりも強力なものになったことを発見しました。
したがって、この1950年代の言語が時代遅れでない理由の簡単な説明は、それが技術ではなく数学であり、数学は古くならないからです。Lispを比較するのに適切なものは、1950年代のハードウェアではなく、例えば1960年に発見されたクイックソートアルゴリズムであり、これは今でも最も速い汎用ソートです。
1950年代から生き残っているもう一つの言語はFortranであり、これは言語設計に対する反対のアプローチを表しています。Lispは理論の一部であり、予期せずプログラミング言語に変わりました。Fortranは意図的にプログラミング言語として開発されましたが、私たちが今考える非常に低レベルのものです。
Fortran I、1956年に開発された言語は、現在のFortranとは非常に異なるものでした。Fortran Iは、数学を伴うほぼアセンブリ言語でした。ある意味では、最近のアセンブリ言語よりも力が劣っていました。例えば、サブルーチンはなく、分岐だけでした。 現在のFortranは、今やLispにかなり近いと言えるでしょう。
LispとFortranは、数学に根ざした一つの進化の木の幹と、機械アーキテクチャに根ざした別の進化の木の幹でした。 これら二つの木はそれ以来収束しています。 Lispは強力に始まり、次の20年間で速くなりました。いわゆる主流の言語は速く始まり、次の40年間で徐々に強力になり、今ではその中で最も進んだものはLispにかなり近いです。 近いですが、まだいくつかのものが欠けています....
Lispを特別にしたもの
最初に開発されたとき、Lispは9つの新しいアイデアを具現化していました。これらのいくつかは今や当たり前のことと考えられていますが、他のものはより進んだ言語でのみ見られ、2つはまだLispに特有です。9つのアイデアは、主流による採用の順に、
条件文。条件文はif-then-else構造です。今ではこれを当たり前のことと考えていますが、Fortran Iにはありませんでした。条件付きgotoのみがあり、これは基盤となる機械命令に基づいていました。
関数型。Lispでは、関数は整数や文字列と同じデータ型です。 それらにはリテラル表現があり、変数に格納でき、引数として渡すことができるなどの特性があります。
再帰。Lispは再帰をサポートする最初のプログラミング言語でした。
動的型付け。Lispでは、すべての変数は実質的にポインタです。値が型を持ち、変数が型を持つのではなく、変数を割り当てたりバインドしたりすることはポインタをコピーすることを意味します。
ガーベジコレクション。
式で構成されたプログラム。Lispプログラムは、各式が値を返す式の木です。 これは、Fortranやほとんどの後続の言語とは対照的で、これらは式と文を区別します。
Fortran Iでは、この区別が自然でした。なぜなら、文をネストすることができなかったからです。 したがって、数学が機能するためには式が必要でしたが、他の何かが値を返す必要はありませんでした。なぜなら、何かがそれを待っていることはできなかったからです。
この制限は、ブロック構造の言語の登場とともに消えましたが、その時には手遅れでした。式と文の区別は根付いていました。それはFortranからAlgolに広がり、両者の子孫に広がりました。
シンボル型。シンボルは、ハッシュテーブルに格納された文字列への実質的なポインタです。したがって、ポインタを比較することで等価性をテストできます。 各文字を比較するのではなく。
シンボルと定数の木を使用したコードの表記法。
全体の言語が常にそこにあります。読み取り時、コンパイル時、実行時の間に実際の区別はありません。 コードを読みながらコンパイルまたは実行したり、コンパイルしながらコードを読みまたは実行したり、実行時にコードを読みまたはコンパイルしたりできます。
読み取り時にコードを実行することで、ユーザーはLispの構文を再プログラムできます。 コンパイル時にコードを実行することはマクロの基礎です。実行時にコンパイルすることは、EmacsのようなプログラムでのLispの拡張言語としての使用の基礎です。そして、実行時に読み取ることは、プログラムがs式を使用して通信できるようにします。このアイデアは最近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が主流の言語が漸近的に近づいている限界を表していると仮定しましょう。実際にそれを使ってソフトウェアを書くべきでしょうか? より弱い言語を使用することでどれだけ失うのでしょうか? 革新の最前線にいることが常に賢明ではないでしょうか? そして、人気はある程度自らの正当化ではないでしょうか?例えば、尖った髪の上司が、簡単にプログラマーを雇える言語を使いたいと思うのは正しいのでしょうか?
もちろん、プログラミング言語の選択があまり重要でないプロジェクトもあります。一般的に、アプリケーションが要求するほど、強力な言語を使用することで得られるレバレッジは大きくなります。しかし、要求が全くないプロジェクトもたくさんあります。 ほとんどのプログラミングは、小さなグルー・プログラムを書くことから成り立っており、小さなグルー・プログラムには、すでに慣れ親しんでいる言語や、必要なことに対して良いライブラリを持っている言語を使用できます。もし、あるWindowsアプリから別のアプリにデータを送信するだけであれば、もちろんVisual Basicを使ってください。
Lispでも小さなグルー・プログラムを書くことができます(私はデスクトップ計算機として使用しています)が、Lispのような言語の最大の利点は、スペクトルの反対側、つまり厳しい競争の中で難しい問題を解決するために洗練されたプログラムを書く必要があるところにあります。 良い例は、ITAソフトウェアがOrbitzにライセンス供与している航空運賃検索プログラムです。 これらの人々は、すでにトラベルシティとエクスペディアという二つの大きな競合他社に支配された市場に参入し、技術的に彼らを屈辱的に打ち負かしたようです。
ITAのアプリケーションの核心は、競合他社がまだメインフレーム時代のプログラミング技術を使用しているのに対し、200,000行のCommon Lispプログラムであり、競合他社よりもはるかに多くの可能性を検索します。 (ただし、ITAもある意味ではメインフレーム時代のプログラミング言語を使用しています。) 私はITAのコードを見たことがありませんが、彼らのトップハッカーの一人によれば、彼らは多くのマクロを使用しているそうで、私はそれを聞いて驚きません。
遠心力
私は、珍しい技術を使用することにコストがないとは言っていません。尖った髪の上司がこれを心配するのは完全に間違っているわけではありません。しかし、彼はリスクを理解していないため、それを誇張する傾向があります。
私は、あまり一般的でない言語を使用することから生じる可能性のある三つの問題を考えています。あなたのプログラムは、他の言語で書かれたプログラムとうまく機能しないかもしれません。利用できるライブラリが少ないかもしれません。そして、プログラマーを雇うのが難しいかもしれません。
これらのそれぞれの問題はどれほどの問題でしょうか?最初の問題の重要性は、あなたがシステム全体を制御しているかどうかによって異なります。もし、バグの多い閉じられたオペレーティングシステムの上でリモートユーザーのマシンで実行する必要があるソフトウェアを書いている場合(名前は挙げませんが)、OSと同じ言語でアプリケーションを書くことには利点があるかもしれません。 しかし、もしあなたがシステム全体を制御し、すべての部分のソースコードを持っている場合、ITAがそうであると仮定すると、あなたは好きな言語を使用できます。もし不整合が生じた場合、あなた自身で修正できます。
サーバーベースのアプリケーションでは、最も進んだ技術を使用することができます。そして、私はこれがジョナサン・エリクソンが呼ぶ「プログラミング言語のルネッサンス」の主な原因だと思います。これが、私たちがPerlやPythonのような新しい言語について聞く理由です。私たちは、これらの言語がWindowsアプリを書くために使用されているからではなく、サーバーで使用されているから聞いています。そして、ソフトウェアがデスクトップからサーバーに移行するにつれて(これはMicrosoftさえも受け入れている未来です)、中道的な技術を使用する圧力はますます少なくなるでしょう。
ライブラリについては、その重要性もアプリケーションによって異なります。要求が少ない問題においては、ライブラリの可用性が言語の内在的な力を上回ることがあります。ブレークイーブンポイントはどこでしょうか?正確には言い難いですが、どこであれ、アプリケーションと呼ばれるものには短すぎるでしょう。もし、ある会社がソフトウェアビジネスにいると考え、彼らが自社の製品の一つになるアプリケーションを書いているのであれば、それはおそらく数人のハッカーを含み、少なくとも6ヶ月はかかるでしょう。その規模のプロジェクトでは、強力な言語が既存のライブラリの便利さを上回り始めるでしょう。
尖った髪の上司の第三の心配、プログラマーを雇うのが難しいということは、私は赤いヘリングだと思います。結局、あなたは何人のハッカーを雇う必要があるのでしょうか?今や、ソフトウェアは10人未満のチームによって最もよく開発されることを私たちは皆知っています。そして、誰もが聞いたことのある言語のスケールでハッカーを雇うのに苦労することはないはずです。もし10人のLispハッカーを見つけられないのであれば、あなたの会社はおそらくソフトウェア開発に適した都市に拠点を置いていないでしょう。
実際、より強力な言語を選ぶことは、必要なチームのサイズを減少させる可能性があります。なぜなら、(a) より強力な言語を使用すれば、必要なハッカーの数が少なくなる可能性が高く、(b) より進んだ言語で働くハッカーは、より賢い可能性が高いからです。
私は、標準的な技術と見なされるものを使用することに対して多くの圧力がかかることは言っていません。Viaweb(現在のYahoo Store)では、Lispを使用していることでVCや潜在的な買収者の間でいくつかの眉をひそめさせました。しかし、私たちはまた、サーバーとして「産業強度」のサーバー(サンのような)ではなく、一般的なIntelボックスを使用していること、実際の商業OS(Windows NTのような)ではなく、当時はあまり知られていなかったオープンソースのUnixバリアントであるFreeBSDを使用していること、そして今では誰も覚えていないようなeコマース標準であるSETを無視していることでも眉をひそめさせました。
スーツに技術的な決定をさせてはいけません。 私たちがLispを使用していたことで、潜在的な買収者の中には驚いた人もいましたか?少しはいましたが、もし私たちがLispを使用していなければ、彼らが私たちを買いたいと思うソフトウェアを書くことはできなかったでしょう。 彼らにとって異常に見えたことは、実際には原因と結果でした。
もしスタートアップを始めるなら、製品をVCや潜在的な買収者を喜ばせるように設計しないでください。ユーザーを喜ばせるように製品を設計してください。 ユーザーを勝ち取れば、他のすべてが続きます。そして、もしそうでなければ、あなたの技術的選択がどれほど快適に正統的であったかは誰も気にしません。
平均であることのコスト
より弱い言語を使用することでどれだけ失うのでしょうか? 実際にそれに関するデータがいくつかあります。
最も便利な力の尺度は、おそらくコードサイズです。 高水準言語の目的は、より大きな抽象を提供することです。つまり、より大きなレンガを提供することで、特定のサイズの壁を構築するために必要な数を減らすことです。 したがって、言語が強力であればあるほど、プログラムは短くなります(もちろん、単に文字数ではなく、異なる要素の数においてです)。
より強力な言語は、どのようにして短いプログラムを書くことを可能にするのでしょうか?言語が許可する場合に使用できる一つの技術は、ボトムアッププログラミングと呼ばれるものです。単に基本言語でアプリケーションを書くのではなく、基本言語の上に自分のようなプログラムを書くための言語を構築し、それでプログラムを書くのです。結合されたコードは、基本言語で全体のプログラムを書いた場合よりもはるかに短くなる可能性があります。実際、これはほとんどの圧縮アルゴリズムが機能する方法です。 ボトムアッププログラムは、変更が容易であるべきです。なぜなら、多くの場合、言語層は全く変更する必要がないからです。
コードサイズは重要です。なぜなら、プログラムを書くのにかかる時間は主にその長さに依存するからです。 もしあなたのプログラムが別の言語で3倍の長さになるなら、書くのに3倍の時間がかかります。そして、これは新しい人を雇うことで回避できるものではありません。なぜなら、あるサイズを超えると、新しい雇用者は実際には純損失になるからです。 フレッド・ブルックスはこの現象を彼の有名な本The Mythical Man-Monthで説明しており、私が見たすべてのことは彼の言ったことを確認する傾向があります。
では、Lispでプログラムを書くと、どれだけ短くなるのでしょうか?例えば、LispとCの比較で聞いた数字のほとんどは、約7-10倍です。 しかし、最近のNew Architect誌の記事では、「1行のLispは20行のCに相当する」と言われており、この数字はITAの社長からの引用が満載だったので、私は彼らがこの数字をITAから得たと推測します。もしそうであれば、私たちはそれに信頼を置くことができます。ITAのソフトウェアにはCやC++が多く含まれているため、彼らは経験から語っています。
私の推測では、これらの倍率は一定ではないと思います。 私は、より難しい問題に直面したときや、より賢いプログラマーがいるときに増加すると考えています。非常に優れたハッカーは、より良いツールからより多くを引き出すことができます。
いずれにせよ、曲線上の一つのデータポイントとして、もしあなたがITAと競争し、Cでソフトウェアを書くことを選んだ場合、彼らはあなたよりも20倍速くソフトウェアを開発できるでしょう。 もしあなたが新しい機能に1年を費やした場合、彼らはそれを3週間未満で複製できるでしょう。彼らが新しいものを開発するのにわずか3ヶ月を費やした場合、あなたがそれを手に入れるまでには5年かかるでしょう。
そして、あなたは何を知っていますか?それは最良のシナリオです。 コードサイズ比について話すとき、あなたは暗黙的に、実際に弱い言語でプログラムを書くことができると仮定しています。 しかし、実際には、プログラマーができることには限界があります。 もしあなたがあまりにも低レベルの言語で難しい問題を解決しようとしている場合、あなたは一度に頭の中に保持するにはあまりにも多くのことに達します。
したがって、ITAがLispで3ヶ月で書けるものをITAの架空の競争相手が5年かかると言うとき、私は何も問題がなければ5年かかると言っています。実際、ほとんどの会社での物事の進行方法では、5年かかる開発プロジェクトは、実際には決して完了しない可能性が高いです。
私はこれが極端なケースであることを認めます。ITAのハッカーは異常に賢いようで、Cはかなり低レベルの言語です。 しかし、競争の激しい市場では、2対1または3対1の差でも、常に後れを取ることが保証されるでしょう。
レシピ
これは、尖った髪の上司が考えたくない可能性の一つです。そして、ほとんどの人はそうしません。 なぜなら、結局のところ、尖った髪の上司は、誰も彼のせいだと証明できない限り、彼の会社が打ち負かされることを気にしないからです。 彼にとって最も安全な計画は、群れの中心に近くに留まることです。
大規模な組織内では、このアプローチを説明するために使用されるフレーズは「業界のベストプラクティス」です。 その目的は、尖った髪の上司を責任から守ることです。もし彼が「業界のベストプラクティス」とされるものを選択し、会社が負けた場合、彼は非難されることはありません。彼は選択しなかった、業界が選択したのです。
この用語は、元々会計方法などを説明するために使用されていたと信じています。おおよそ言うと、何か奇妙なことをしないでくださいという意味です。そして、会計においてはそれはおそらく良いアイデアです。「最先端」と「会計」という用語は一緒に聞こえません。しかし、この基準を技術に関する決定に持ち込むと、間違った答えが得られ始めます。
技術はしばしば最先端であるべきです。プログラミング言語において、エラン・ガットが指摘したように、「業界のベストプラクティス」が実際に得られるものは、最良ではなく、単に平均です。ある決定が、より攻撃的な競合他社の数分の一の速度でソフトウェアを開発させる場合、「ベストプラクティス」は誤称です。
したがって、ここには私が非常に価値があると思う二つの情報があります。実際、私は自分の経験からそれを知っています。 1つ目、言語は力において異なります。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言語では、限られた範囲で、クローズャ(囲まれたスコープで定義された変数を参照する関数)をシミュレートすることができます。これは、各変数を囲まれたスコープから置き換えるためのメソッドとフィールドを持つクラスを定義することによって行われます。これは、完全なレキシカルスコープをサポートする言語ではコンパイラが行うようなコード分析をプログラマーに強いるものであり、同じ変数を参照する複数の関数がある場合には機能しませんが、このような単純なケースでは十分です。
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での「業界のベストプラクティス」に関する悲しい話は、私がこの一般的に誤用されているフレーズに取り組むきっかけとなりました。
ピーター・ノーヴィグは、Design Patternsの23のパターンのうち16がLispでは「見えないか、より単純」であることを発見しました。
私の質問に答えてくれた多くの人々、さまざまな言語についての質問に答えてくれた人々、またはこの草稿を読んでくれた人々に感謝します。ケン・アンダーソン、トレバー・ブラックウェル、エラン・ガット、ダン・ギフィン、サラ・ハーリン、ジェレミー・ハイルトン、ロバート・モリス、ピーター・ノーヴィグ、ガイ・スティール、アントン・ファン・ストラーテンを含みます。 彼らは表現された意見に対して責任を負いません。
関連:
多くの人々がこの講演に反応したので、彼らが提起した問題に対処するための追加ページを設けました: Re: Revenge of the Nerds。
また、LL1 メーリングリストで広範囲にわたる、しばしば有益な議論が始まりました。特に、セマンティック圧縮に関するアントン・ファン・ストラーテンのメールを参照してください。
LL1のいくつかのメールは、Succinctness is Power における言語の力の主題をより深く掘り下げるきっかけとなりました。
accumulator generator benchmark の標準的な実装の大規模なセットが、独自のページにまとめられています。
Japanese Translation、Spanish Translation、Chinese Translation