Loading...

オタクたちの復讐

Original

2002年5月

「私たちはC++プログラマーを狙っていました。なんとかして、彼らの多くをLispに引きずり込むことに成功しました。」

  • Guy Steele、Java仕様の共著者

ソフトウェア業界では、頭が尖った学者と、もうひとつの同様に手ごわい勢力である、頭が尖った上司との間で、絶え間ない闘争が繰り広げられています。頭が尖った上司が誰かは、みんな知っていますよね?テクノロジーの世界にいるほとんどの人は、この漫画のキャラクターを認識するだけでなく、自分の会社にいる、このキャラクターのモデルになった人物を知っていると思います。

頭が尖った上司は、それぞれ単独ではよくあることですが、一緒に見られることはめったにない、2つの性質を奇跡的に兼ね備えています。(a)彼はテクノロジーについて何も知りません。(b)彼はテクノロジーについて非常に強い意見を持っています。

たとえば、ソフトウェアを記述する必要があるとします。頭が尖った上司は、このソフトウェアがどのように動作するのか全く理解しておらず、プログラミング言語を区別することもできません。にもかかわらず、彼はどの言語で記述すべきかを知っています。そうです。彼はJavaで記述すべきだと考えています。

なぜ彼はそう考えるのでしょうか?頭が尖った上司の脳の中をのぞいてみましょう。彼が考えているのは、このようなことです。Javaは標準です。私は、新聞で頻繁に目にするので、標準であるに違いありません。標準であるため、使用しても問題ありません。また、常に多くのJavaプログラマーがいることを意味します。そのため、現在私のために働いているプログラマーが、私のために働くプログラマーがいつもそうであるように、謎めいた方法で辞めても、簡単に彼らを交代させることができます。

まあ、これはそれほど不合理には聞こえません。しかし、それはすべて、ひとつの暗黙の仮定に基づいており、その仮定は実際には間違っています。頭が尖った上司は、すべてのプログラミング言語はほぼ同等であると考えています。もしそれが本当なら、彼は正しく目標を定めているでしょう。言語がすべて同等であれば、確かに、他の誰もが使用している言語を使用してください。

しかし、すべての言語が同等というわけではなく、私は、言語間の違いについて触れることなく、あなたにそれを証明できると信じています。1992年に頭が尖った上司に、ソフトウェアをどの言語で記述すべきかと尋ねたら、彼は今日と同じようにためらうことなく答えたでしょう。ソフトウェアはC++で記述すべきです。しかし、言語がすべて同等であるなら、なぜ頭が尖った上司の意見は変わるのでしょうか?実際、なぜJavaの開発者は、新しい言語を作成する必要があったのでしょうか?

おそらく、新しい言語を作成するのは、それが既存のものよりも何らかの点で優れていると考えるからです。実際、Goslingは最初のJavaのホワイトペーパーで、JavaはC++のいくつかの問題を解決するために設計されたと明確に述べています。つまり、言語はすべて同等ではありません。頭が尖った上司の脳の中のJavaへの道筋をたどり、そこからJavaの歴史を遡ってその起源までたどると、出発点の仮定と矛盾する考えにたどり着きます。

では、どちらが正しいのでしょうか?James Goslingか、頭が尖った上司か?驚くべきことに、Goslingが正しいのです。いくつかの言語は、特定の問題に対して、他の言語よりも優れています。そして、それは興味深い疑問を提起します。Javaは、特定の問題に対して、C++よりも優れているように設計されました。どのような問題でしょうか?Javaが優れているのはいつで、C++が優れているのはいつでしょうか?他の言語が、どちらよりも優れている状況はあるのでしょうか?

この質問を検討し始めると、あなたは本当に厄介な問題に突き当たります。頭が尖った上司が、問題全体をその複雑さで考えなければならなかったら、彼の脳は爆発してしまうでしょう。彼がすべての言語を同等であると考える限り、彼がしなければならないのは、最も勢いがあるように見える言語を選ぶだけです。そして、それはテクノロジーよりもむしろ流行の問題なので、彼でさえおそらく正しい答えを得ることができるでしょう。しかし、言語が異なれば、彼は突然、2つの連立方程式を解かなければなりません。彼は、自分が何も知らない2つのことの間で最適なバランスを見つけようとしています。それは、自分が解決する必要がある問題に対する20ほどの主要な言語の相対的な適性と、各言語のプログラマー、ライブラリなどを探す確率です。もし、ドアの向こう側にそのようなものがあるなら、頭が尖った上司がそれを開けたくないのも不思議ではありません。

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

Javaはクールで新しいプログラミング言語なので、かなり良いものであることはわかっています。それともそうでしょうか?プログラミング言語の世界を遠くから見ると、Javaは最新のもののようです。(十分に遠くから見ると、見えるのはサンが支払った大きな点滅する看板だけです。) しかし、この世界を 間近で見ると、クールさの度合いがあることに気づきます。 ハッカーのサブカルチャーの中では、Perlと呼ばれる別の言語があり、Javaよりもはるかにクールだと考えられています。たとえば、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関数eval...であり、 Lisp式の値を計算します.... evalを記述するには、Lisp関数をLispデータとして表す表記法を発明する必要があり、そのような表記法は、 論文の目的で考案され、Lispプログラムを実際的に表現するために使用されることは想定されていませんでした。

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

これは当時、大きな驚きでした。 マッカーシーは後にインタビューで次のように述べています。

スティーブ・ラッセルは、「このevalをプログラムしてみたらどうだろう?」と言いました。 私は彼に、「おやおや、理論と実践を混同しているぞ。このevalは計算するためではなく、読むためのもんだ」と言いました。しかし、彼はそれを実行しました。つまり、彼は論文のevalを[IBM] 704の機械語にコンパイルし、バグを修正し、それをLispインタプリタとして宣伝しました。確かに、それはLispインタプリタでした。その時点で、Lispは 今日あるような形になりました....

突然、数週間のうちに、マッカーシーは自分の理論的な 練習が実際のプログラミング言語に変換されたことに気づきました。それは、彼が意図していたよりも強力な言語でした。

1950年代の言語が時代遅れになっていない理由を簡単に説明すると、それは技術ではなく数学であり、数学は時代遅れにならないからです。Lispを比較すべきなのは、1950年代のハードウェアではなく、例えば1960年に発見され、現在でも最も高速な汎用ソートであるクイックソートアルゴリズムです。

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

Fortran Iは、1956年に開発された言語であり、今日のFortranとは全く異なるものでした。Fortran Iは、ほとんど数学を伴うアセンブリ言語でした。ある意味では、最近の多くのアセンブリ言語よりも強力ではありませんでした。例えば、サブルーチンはなく、分岐のみでした。今日のFortranは、Fortran IよりもLispに近いと言えるでしょう。

LispとFortranは、数学に根ざした1つと、マシンアーキテクチャに根ざした1つの、2つの別々の進化の木の幹でした。これらの2つの木は、それ以来収束しています。Lispは強力なスタートを切り、その後20年間で高速になりました。いわゆる主流言語は、高速なスタートを切り、その後40年間で徐々に強力になり、現在では最も高度な言語はLispにかなり近いものになっています。近いですが、まだいくつか欠けているものがあります...

Lispを特別なものにしたもの

Lispは、最初に開発されたとき、9つの新しいアイデアを体現していました。これらのいくつかは、今では当たり前のものになっていますが、他のいくつかはより高度な言語でのみ見られ、2つはLispに固有のものです。9つのアイデアは、主流への採用順に、

条件式。条件式は、if-then-else構造です。今では当たり前のものになっていますが、Fortran Iにはありませんでした。Fortran Iには、基になるマシン命令に基づいた条件付きgotoのみがありました。

関数型。Lispでは、関数は整数や文字列と同じようにデータ型です。関数は、リテラル表現を持ち、変数に格納することができ、引数として渡すことができます。

再帰。Lispは、再帰をサポートした最初のプログラミング言語でした。

動的型付け。Lispでは、すべての変数は事実上ポインタです。値には型がありますが、変数には型がなく、変数への代入またはバインドは、ポインタのコピーを意味し、ポインタが指すものをコピーするわけではありません。

ガベージコレクション。

式で構成されたプログラム。Lispプログラムは、式のツリーで構成されており、各式は値を返します。これは、Fortranやその後のほとんどの言語とは対照的です。Fortranやその後のほとんどの言語では、式と文を区別しています。

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

この制限は、ブロック構造言語の登場によって解消されましたが、その時にはすでに手遅れでした。式と文の区別は、確立されていました。FortranからAlgolに、そして両方の子孫に広がりました。

シンボル型。シンボルは、ハッシュテーブルに格納された文字列へのポインタです。そのため、各文字を比較するのではなく、ポインタを比較することで等価性をテストすることができます。

シンボルと定数のツリーを使用したコードの表記。

言語全体が常に存在する。読み込み時、コンパイル時、実行時の間に、実際には区別がありません。読み込み時にコードをコンパイルまたは実行したり、コンパイル時にコードを読み込んだり実行したり、実行時にコードを読み込んだりコンパイルしたりすることができます。

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

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

番号 8 については、これが最も興味深いものです。 アイデア 8 と 9 は、Steve Russell が McCarthy が実装することを意図していなかったものを実装したため、偶然 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 アプリから別の Windows アプリにデータを供給する必要があるだけの場合、Visual Basic を使用してください。

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

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

求心力

私は、珍しい技術を使用することにはコストがかからないとは言いません。ハゲ頭のボスがこれについて心配するのは、完全に間違っているわけではありません。しかし、彼はリスクを理解していないため、リスクを過大評価しがちです。

あまり一般的ではない言語を使用することによって発生する可能性のある問題を3つ考えられます。プログラムが他の言語で書かれたプログラムと連携しない可能性があります。利用できるライブラリが少なくなる可能性があります。そして、プログラマーの採用が困難になる可能性があります。

これらの問題はどの程度深刻なのでしょうか?最初の問題の重要性は、システム全体を制御できるかどうかによって異なります。バグのあるクローズドオペレーティングシステム(名前は出しませんが)上で、リモートユーザーのマシンで実行する必要があるソフトウェアを作成する場合、アプリケーションをOSと同じ言語で記述することには利点があるかもしれません。 しかし、ITAがしているように、システム全体を制御し、すべての部品のソースコードを持っている場合は、好きな言語を使用できます。互換性の問題が発生した場合、自分で修正できます。

サーバーベースのアプリケーションでは、最も高度なテクノロジーを使用しても問題ありません。そして、私はこれが、Jonathan Ericksonが「プログラミング言語のルネサンス」と呼ぶものの主な原因であると考えています。これが、PerlやPythonのような新しい言語について耳にする理由です。これらの言語について耳にするのは、人々がWindowsアプリケーションを作成するために使用しているからではなく、サーバーで使用しているからです。そして、ソフトウェアがデスクトップからサーバーに移行するにつれて(マイクロソフトでさえ、この未来を受け入れているようです)、中庸なテクノロジーを使用する圧力はますます減少していくでしょう。

ライブラリに関しては、その重要性もアプリケーションによって異なります。要求の厳しい問題ではない場合、ライブラリの可用性は、言語の本質的なパワーよりも重要になる可能性があります。採算点がどこにあるのでしょうか?正確に言うのは難しいですが、どこであれ、アプリケーションと呼ぶにはほど遠いものです。会社がソフトウェアビジネスだと考えていて、製品の1つとなるアプリケーションを作成している場合、それはおそらく複数のハッカーを必要とし、少なくとも6か月はかかるでしょう。そのような規模のプロジェクトでは、強力な言語は、既存のライブラリの利便性よりも重要になる可能性があります。

ハゲ頭のボスの3つ目の懸念である、プログラマーの採用が難しいという点は、私は赤 herring だと思います。結局のところ、何人のハッカーを雇う必要があるのでしょうか?今では、ソフトウェアは10人未満のチームで開発するのが最適であることは、誰もが知っているはずです。そして、誰もが知っている言語であれば、その規模のハッカーを雇うのに苦労することはないはずです。10人のLispハッカーが見つからない場合は、あなたの会社はソフトウェア開発に適した都市に拠点を置いていないのでしょう。

実際、より強力な言語を選択すると、必要なチームの規模が縮小する可能性があります。なぜなら、(a) より強力な言語を使用すると、それほど多くのハッカーは必要ない可能性があり、(b) より高度な言語で作業するハッカーは、より賢い可能性があるからです。

「標準」と見なされているテクノロジーを使用するように、多くのプレッシャーがかかることは言うまでもありません。Viaweb(現在のYahoo Store)では、Lispを使用することで、VCや潜在的な買収者から眉をひそめられました。しかし、私たちはまた、Sunのような「産業用強度」のサーバーではなく、汎用的なIntelボックスをサーバーとして使用したことで、Windows NTのような本格的な商用OSではなく、当時無名だったオープンソースのUnix変種であるFreeBSDを使用したことで、誰も覚えていない「SET」と呼ばれる電子商取引の標準を無視したことで、眉をひそめられました。SETなどです。

スーツに技術的な意思決定をさせてはいけません。 Lispを使ったことで、潜在的な買収者を不安にさせたのでしょうか?一部は少し不安に思ったかもしれませんが、もしLispを使っていなければ、私たちを買収したいと思わせるようなソフトウェアを書くことはできなかったでしょう。 彼らにとって異常に見えたものは、実際には因果関係でした。

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

平均的なことの代償

あまり強力でない言語を使うことで、どれだけの損失があるのでしょうか? 実際、それに関するデータがいくつかあります。

最も便利な力の尺度は、おそらくコードサイズでしょう。 高レベル言語の目的は、より大きな抽象化を提供することです。つまり、より大きなレンガを提供することで、ある大きさの壁を建てるために必要なレンガの数を減らすことができます。 したがって、言語が強力であればあるほど、プログラムは短くなります(文字数だけでなく、個別の要素においても)。

より強力な言語は、どのようにしてより短いプログラムを書くことを可能にするのでしょうか?言語が許すのであれば、使用できるテクニックの1つは、ボトムアッププログラミングと呼ばれるものです。 単にベース言語でアプリケーションを書くのではなく、ベース言語の上に、あなたのプログラムのようなプログラムを書くための言語を構築し、その言語でプログラムを書きます。 組み合わせたコードは、ベース言語でプログラム全体を書いた場合よりもはるかに短くなる可能性があります。実際、これはほとんどの圧縮アルゴリズムが動作する方法です。 ボトムアッププログラムは、変更も容易になるはずです。なぜなら、多くの場合、言語レイヤーはまったく変更する必要がないからです。

コードサイズは重要です。なぜなら、プログラムを書くのにかかる時間は、ほとんどその長さによって決まるからです。 もしあなたのプログラムが別の言語で3倍の長さになるなら、書くのに3倍の時間がかかります。そして、これは、より多くの人を雇うことによって回避することはできません。なぜなら、ある規模を超えると、新しい雇用は実際には純損失になるからです。 フレッド・ブルックスは、彼の有名な著書「人月の神話」の中でこの現象について説明しており、私が見てきたすべてのものは、彼が言ったことを確認する傾向があります。

では、Lispでプログラムを書くと、プログラムはどれくらい短くなるのでしょうか? たとえば、LispとCの比較で私が聞いたほとんどの数字は、約7〜10倍でした。 しかし、New Architect誌のITAに関する最近の論文では、「Lispの1行はCの20行に相当する」と述べており、この論文はITAの社長からの引用でいっぱいだったので、この数字はITAから得られたものだと推測しています。 もしそうなら、私たちはそれをある程度信頼することができます。ITAのソフトウェアには、Lispだけでなく、CやC++もたくさん含まれているため、彼らは経験に基づいて話しています。

私の推測では、これらの倍率は一定ではありません。 私は、より難しい問題に直面した場合や、より賢いプログラマーがいる場合に、倍率が増加すると考えています。本当に優れたハッカーは、より良いツールからより多くのものを絞り出すことができます。

いずれにせよ、データポイントの1つとして、もしあなたがITAと競争し、ソフトウェアをCで書くことを選択した場合、彼らはあなたよりも20倍速くソフトウェアを開発することができます。 あなたが新しい機能に1年間費やした場合、彼らは3週間以内にそれを複製することができます。一方、彼らが新しいものを開発するためにわずか3か月費やした場合、あなたがそれを手に入れるまでには5年かかります。

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

したがって、私がITAの架空の競合他社が、ITAがLispで3か月で書けるものを複製するのに5年かかるだろうと言うとき、それは何も問題がなければ5年かかるという意味です。 実際、ほとんどの企業では、5年かかる開発プロジェクトは、そもそも完了しない可能性が高いです。

これは極端な例だと認めます。ITAのハッカーは異常に賢く、Cは非常に低レベルな言語です。しかし、競争の激しい市場では、2〜3倍の差でも、常に後れを取ることの保証になります。

レシピ

これは、尖った髪のボスが考えたくもないような可能性です。そして、ほとんどのボスはそうしません。なぜなら、結局のところ、尖った髪のボスは、自分の会社が蹴りつけられても、それが自分のせいだと証明されない限り、気にしません。彼にとって最も安全な計画は、群れの真ん中に近いところにいることです。

大規模な組織内では、このアプローチを説明するために使用されるフレーズは「業界のベストプラクティス」です。その目的は、尖った髪のボスを責任から守ることです。彼が「業界のベストプラクティス」を選択し、会社が負けた場合、彼は非難されることはありません。彼が選んだのではなく、業界が選んだのです。

この用語は、もともと会計方法などを説明するために使用されていたと思います。大まかに言えば、それは奇妙なことは何もするなということです。そして、会計では、それはおそらく良い考えです。「最先端」と「会計」という用語は、一緒に聞こえません。しかし、この基準をテクノロジーに関する意思決定に輸入すると、間違った答えが出始めます。

テクノロジーは、しばしば最先端であるべきです。Erann Gatが指摘しているように、プログラミング言語では、「業界のベストプラクティス」が実際に得られるのは、最高ではなく、単なる平均です。意思決定が、より積極的な競合他社の速度のほんの一部でソフトウェアを開発することを引き起こす場合、「ベストプラクティス」は誤った名称です。

そこで、私は非常に価値があると考える2つの情報があります。実際、私は自分の経験からそれを知っています。1つ目は、言語のパワーは異なるということです。2つ目は、ほとんどのマネージャーはこの事実を意図的に無視しているということです。これらの2つの事実は、文字通り、お金を稼ぐためのレシピです。ITAは、このレシピが実際に機能している例です。 ソフトウェアビジネスで勝ちたい場合は、見つけられる最も難しい問題に取り組み、手に入る最も強力な言語を使用し、競合他社の尖った髪のボスが平均に逆戻りするのを待ちましょう。

付録:パワー

プログラミング言語の相対的なパワーについて私が何を意味するのかを説明するために、次の問題を考えてみましょう。 私たちは、アキュムレーターを生成する関数を書きたいです。つまり、数値nを受け取り、 別の数値iを受け取って、iでnを増分した値を返す関数です。

(これはiで増分した、プラスではありません。アキュムレーターは蓄積する必要があります。)

Common Lispでは、これは次のようになります。


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

そして、Perl 5では、


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

これは、Lispバージョンよりも要素が多いです。なぜなら、Perlではパラメーターを手動で抽出する必要があるからです。

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も この区別を保持していますが、典型的な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言語では、ある程度まで、クロージャ(囲まれたスコープで定義された変数を参照する関数)を、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)では、実際にこの問題を解決できるかどうかは明らかではありません。 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のハッカーとの多くのメールのやり取りの後、私は、前の例のように動作する適切な多態的なバージョンを書くことは、非常に厄介か不可能かのどちらかであると言うでしょう。誰かがそれを書きたいなら、私はそれをとても見たいですが、私は個人的にタイムアウトしました。

もちろん、他の言語でこの問題を解決できないというのは、文字通りではありません。これらの言語はすべてチューリング完全であるという事実は、厳密に言えば、どの言語でも任意のプログラムを書くことができることを意味します。では、どのようにすればよいでしょうか?限界ケースでは、より強力な言語でLispインタプリタを書くことで。

それはジョークのように聞こえますが、大規模なプログラミングプロジェクトでは、さまざまな程度で頻繁に起こるため、この現象には名前があります。グリーンスタインの第10法則:

十分に 複雑なCまたはFortranプログラムには、Common Lispの半分をアドホックに、非公式に指定された、バグのある、遅い実装が含まれています。

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

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

注記

IBM 704 CPUは冷蔵庫ほどの大きさでしたが、はるかに重かったです。CPUの重量は3150ポンドで、4KのRAMは別の箱に収納されており、さらに4000ポンドの重さがありました。Sub-Zero 690は、最も大きな家庭用冷蔵庫の1つで、重量は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 _])

Erann Gatの「業界のベストプラクティス」に関する悲しい話は、JPLでこの一般的に誤用されているフレーズに対処するよう促しました。

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

さまざまな言語に関する質問に答えてくれたり、この原稿の草稿を読んでくれた多くの人々に感謝します。ケン・アンダーソン、トレバー・ブラックウェル、エラン・ガット、ダン・ギフィン、サラ・ハーリン、ジェレミー・ハイルトン、ロバート・モリス、ピーター・ノーヴィッグ、ガイ・スティール、アントン・ファン・ストラテンです。 彼らは、表明された意見について責任を負いません。

関連:

多くの人がこの講演に反応したので、彼らが提起した問題に対処するために追加のページを設定しました。Re: Revenge of the Nerds

また、LL1メーリングリストで、広範囲にわたる、そしてしばしば役立つ議論を引き起こしました。特に、アントン・ファン・ストラテンによる意味圧縮に関するメールを参照してください。

LL1に関するメールの一部は、Succinctness is Powerで言語の力についてさらに深く掘り下げようとするきっかけとなりました。

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

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