Loading...

簡潔さは力なり

Original

2002年5月

「代数記号によって小さなスペースに圧縮された意味の量は、それらの助けを借りて私たちが慣れ親しんでいる推論を容易にするもう 1 つの状況です。」

  • チャールズ・バベッジ、アイバーソンのチューリング賞講演で引用

LL1 メーリング リストでRevenge of the Nerdsが提起した問題についての議論の中で、Paul Prescod が書いた内容が私の心に残りました。

Python の目標は簡潔さではなく、規則性と読みやすさです。

一見すると、これはプログラミング言語について主張するにはかなり非難すべきことのように思えます。私の知る限り、簡潔さ=力です。もしそうなら、次のように置き換えます。

Python の目標は、パワーではなく、規則性と可読性です。

そして、これは(トレードオフであるとしても)望ましいトレードオフではないようです。Python の目標は、プログラミング言語として効果的になることではないと言っても過言ではありません。

簡潔さ = 力でしょうか? これは私にとって重要な質問のように思えます。言語設計に関心のある人にとってはおそらく最も重要な質問であり、直接対峙することが有益な質問です。答えが単純に「はい」であるかどうかはまだ確信が持てませんが、出発点としては良い仮説のように思えます。

仮説

私の仮説は、簡潔さは力である、あるいは病的な例を除いて、簡潔さと力は十分近いので、それらを同一として扱うことができる、というものです。

簡潔さこそがプログラミング言語*の目的であるように私には思えます。*コンピューターは、何をすべきかを機械語で直接指示されても、同じように喜んでくれるでしょう。私たちがわざわざ高級言語を開発する主な理由は、機械語では 1000 行必要となることを高級言語の 10 行で表現 (そしてもっと重要なことに、考える) できるように、影響力を得るためだと思います。言い換えれば、高級言語の主な目的は、ソース コードを小さくすることです。

高級言語の目的がソース コードを小さくすることであり、何かの威力はそれがどれだけ目的を達成するかであるならば、プログラミング言語の威力の尺度は、プログラムをどれだけ小さくできるかです。

逆に言えば、プログラムを小さくできない言語は、切れ味の悪いナイフや判読しにくい印刷物のように、プログラミング言語が果たすべき役割を十分に果たしていないことになります。

メトリクス

では、どのような意味で小さいのでしょうか? コード サイズの最も一般的な測定基準は、コード行数です。しかし、この測定基準が最も一般的であるのは、測定が最も簡単だからだと思います。これがプログラムの長さの真の基準であると本当に信じている人はいないと思います。言語によって、1 行に記述する内容の慣例が異なります。C では、多くの行には 1 つまたは 2 つの区切り文字以外は何も記述されません。

もう一つの簡単なテストはプログラム内の文字数ですが、これもあまり良くありません。一部の言語 (たとえば Perl) では、他の言語よりも短い識別子を使用するだけです。

プログラムのサイズを測るには、要素の数のほうがよいと思います。ここで要素とは、ソース コードを表すツリーを描いた場合に個別のノードとなるものです。変数または関数の名前は要素です。整数または浮動小数点数は要素です。リテラル テキストのセグメントは要素です。パターンまたはフォーマット ディレクティブの要素は要素です。新しいブロックは要素です。境界線上のケース (-5 は 2 つの要素か 1 つの要素か) もありますが、ほとんどのケースはどの言語でも同じなので、比較にはあまり影響しません。

この測定基準は具体化が必要であり、特定の言語の場合は解釈が必要になる可能性がありますが、プログラムに含まれるパーツの数という正しいものを測定しようとしていると思います。この演習で描くツリーは、プログラムを思いつくために頭の中で作らなければならないものであり、したがってそのサイズは、それを書いたり読んだりするために必要な作業量に比例すると思います。

デザイン

この種の測定基準により、異なる言語を比較することができますが、少なくとも私にとっては、それがその主な価値ではありません。簡潔性テストの主な価値は、言語を設計する際のガイドとしてです。言語間の最も有用な比較は、同じ言語の 2 つの潜在的なバリエーションの比較です。プログラムを短くするために、言語で何ができるでしょうか。

プログラムの概念的負荷がその複雑さに比例し、特定のプログラマーが一定の概念的負荷に耐えられるとしたら、これは「プログラマーが最大限の成果を出せるようにするにはどうすればよいか」と尋ねるのと同じです。そしてそれは、「どうすれば良い言語を設計できるか」と尋ねるのと同じように思えます。

(ちなみに、言語を設計する以上に「すべての言語は同等である」という古い決まり文句が間違っていることが明白になることはありません。新しい言語を設計するときは、常に2 つの言語 (x を実行した場合の言語と実行しなかった場合の言語) を比較して、どちらが優れているかを判断します。これが本当に無意味な質問であるなら、コインを投げるのと同じでしょう。)

簡潔さを目指すことは、新しいアイデアを見つける良い方法のように思えます。多くの異なるプログラムを短くできる方法があれば、それはおそらく偶然ではありません。おそらく、役に立つ新しい抽象化を発見したのでしょう。ソース コードで繰り返しパターンを検索することで、役立つプログラムを書くこともできるかもしれません。他の言語の中でも、簡潔さで定評のある言語は、新しいアイデアを探すのに頼るべき言語です。Forth、Joy、Icon などです。

比較

私が知る限り、これらの問題について最初に書いたのは、 Mythical Man Monthの Fred Brooks です。彼は、プログラマーは言語に関係なく、1 日にほぼ同じ量のコードを生成するようだと書いています。20 代前半にこれを初めて読んだとき、私は大きな驚きを感じ、大きな意味があるように思えました。それは、(a) ソフトウェアをより速く記述する唯一の方法は、より簡潔な言語を使用することであり、(b) そのために苦労した人は、そうしなかった競争相手を置き去りにできるということを意味していました。

ブルックスの仮説は、もしそれが真実なら、ハッキングの核心であるように思われる。それ以来、私は、正式な研究から個々のプロジェクトに関する逸話まで、この問題に関して得られるあらゆる証拠に細心の注意を払ってきた。彼の主張に反するものは何も見当たらない。

決定的と思われる証拠はまだ見ていませんし、今後も見られないと思います。Lutz Prechelt によるプログラミング言語の比較などの研究では、私が期待していた種類の結果が得られたものの、意味のあるテストとしては短すぎる問題が使用される傾向があります。言語のより良いテストは、記述に 1 か月かかるプログラムで何が起こるかです。そして、言語の主な目的が (単に考えついたことをコンピューターに指示するのではなく) 考えやすいことであると私が信じている場合、唯一の本当のテストは、その言語でどのような新しいものを書くことができるかです。したがって、事前に定義された仕様を満たす必要がある言語の比較は、少し間違ったことをテストしていることになります。

言語の真のテストは、新しい問題をどれだけうまく発見して解決できるかであり、他の誰かがすでに定式化した問題をどれだけうまく解決できるかではありません。この 2 つはまったく異なる基準です。芸術では、刺繍やモザイクなどの媒体は、事前に何を作りたいかがわかっている場合はうまく機能しますが、わからない場合はまったく役に立ちません。作成しながらイメージを発見したい場合 (たとえば、人物のイメージのように複雑なものの場合)、鉛筆、水墨画、油絵の具など、より流動的な媒体を使用する必要があります。実際、タペストリーやモザイクは、最初に絵を描き、次にそれをコピーするという方法で実際に作成されます (「漫画」という言葉は、もともとこの目的のために作成された絵画を表すために使用されていました)。

これが意味するのは、プログラミング言語の相対的な力の正確な比較は、おそらく決してできないということです。正確な比較はできるでしょうが、正確な比較ではありません。特に、言語を比較する目的での明示的な研究では、おそらく小さな問題が使用され、必然的に定義済みの問題が使用されるため、より強力な言語の力を過小評価する傾向があります。

現場からの報告は、必然的に「科学的」な研究ほど正確ではないものの、より有意義である可能性が高い。たとえば、Ericsson の Ulf Wiger は、Erlang は C++ よりも 4 ~ 10 倍簡潔で、ソフトウェア開発がそれに比例して高速であるという結論を下した研究を行った。

Ericsson 社内の開発プロジェクトを比較すると、どの言語 (Erlang、PLEX、C、C++、または Java) が使用されたかに関係なく、ソフトウェア開発のすべてのフェーズを含む行数/時間の生産性は同様であることがわかります。異なる言語を区別するのは、ソース コードの量です。

この研究は、ブルックス氏の本では暗黙的にしか述べられていなかった点(デバッグされたコードの行数を計測していたため)についても明示的に扱っている。つまり、より強力な言語で書かれたプログラムはバグが少ない傾向があるということだ。これは、ネットワーク スイッチなどのアプリケーションでは、それ自体が目的となり、おそらくプログラマーの生産性よりも重要になる。

味覚テスト

最終的には、直感に従うしかないと思います。その言語でプログラミングするとどんな感じがしますか? 最適な言語を見つける (または設計する) 方法は、その言語でどれだけうまく考えることができるかに敏感になり、最も使いやすい言語を選択/設計することだと思います。言語の機能が使いにくかったり制限があったりしても、心配しないでください。すぐにわかるはずです。

このような過敏さには代償が伴います。扱いにくい言語でのプログラミングに耐えられなくなるでしょう。マクロのない言語でのプログラミングは、私にとっては耐え難いほどの制約です。動的型付けに慣れている人にとっては、すべての変数の型を宣言する必要があり、異なる型のオブジェクトのリストを作成できない言語でのプログラミングに戻らなければならないのは耐え難いほどの制約です。

私だけではありません。同じ経験をした Lisp ハッカーを何人も知っています。実際、プログラミング言語の相対的な力を最も正確に測る基準は、その言語を知っている人のうち、アプリケーション ドメインに関係なく、その言語を使用できる仕事であれば何でも引き受ける人の割合かもしれません。

制限性

言語が制限されていると感じるということはどういうことか、ほとんどのハッカーは知っていると思います。そのように感じるとき、何が起きているのでしょうか? 行きたい道が封鎖されていて、行きたい場所にたどり着くまでに長い遠回りをしなくてはならないときと同じ感覚だと思います。言いたいことがあるのに、言語がそれを許してくれないのです。

ここで実際に起こっていることは、制限のある言語は簡潔さが足りないということだと思います。問題は、単に計画したことを表現できないということだけではありません。言語によって迂回する回数が*長くなるということです。次の思考実験を試してみてください。書きたいプログラムがあるとします。言語では計画した通りに表現できず、代わりにもっと短い方法でプログラムを書くように強制されます。*少なくとも私にとっては、それほど制限があるとは感じません。行きたい道が封鎖され、交差点の警官が迂回する代わりに近道を案内してくれるようなものです。すばらしい!

制限されていると感じることのほとんど (90 パーセント?) は、その言語で書いたプログラムを、頭の中で思い描いているプログラムよりも長くしなければならないことから来ていると思います。制限されているというのは、ほとんどの場合、簡潔さが欠けているということです。つまり、言語が制限されていると感じるとき、それは (ほとんどの場合) 十分に簡潔ではないということを意味し、言語が簡潔でないと、制限されていると感じることになります。

読みやすさ

冒頭の引用では、規則性と読みやすさという 2 つの特性について言及しています。規則性とはどのようなものか、規則性があり読みやすいコードが、単に読みやすいだけのコードと比べてどのような利点があるのかはわかりません。しかし、読みやすさの意味はわかっていると思いますし、簡潔さとも関係していると思います。

ここで、個々のコード行の読みやすさとプログラム全体の読みやすさを区別するように注意する必要があります。重要なのは後者です。Basic の 1 行は Lisp の 1 行よりも読みやすい可能性が高いことには同意します。しかし、Basic で書かれたプログラムは、Lisp で書かれた同じプログラムよりも行数が多くなります (特に Greenspunland に入ると)。Basic プログラムを読むための総労力は、間違いなく大きくなります。

総労力 = 1 行あたりの労力 x 行数

読みやすさが簡潔さに直接比例するかどうかは、パワーが直接比例するかどうかほど確信はありませんが、簡潔さは確かに読みやすさの要素です (数学的な意味で、上記の式を参照)。したがって、言語の目標は簡潔さではなく読みやすさであると言うことは意味がないかもしれません。目標は読みやすさではなく読みやすさであると言うようなものです。

行ごとの可読性が実際に意味するのは、その言語に初めて触れるユーザーにとって、ソース コードが脅威に見えないということです。したがって、行ごとの可読性は、設計上の決定としては悪いものであっても、マーケティング上の決定としては良いものになる可能性があります。これは、分割払いという非常に成功した手法と同型です。高額な初期費用でユーザーを怖がらせる代わりに、月々の支払額が低いことを伝えるのです。ただし、分割払いは購入者にとって純然たる損失であり、行ごとの可読性だけではおそらくプログラマーにとって損失です。購入者は、非常に低い支払いを何も行うことになり、プログラマーは、個別に読み取れる行を何度も読むことになります。

このトレードオフはプログラミング言語より前から存在しています。小説や新聞記事を読むことに慣れている人にとって、数学の論文を初めて読む経験はがっかりするかもしれません。1 ページを読むのに 30 分かかることもあります。それでも、表記法が問題ではないことはほぼ間違いありません (そう感じるかもしれませんが)。数学の論文が読みにくいのは、考えが難しいからです。同じ考えを散文で表現したとしても (簡潔な表記法が発達する前の数学者はそうしなければなりませんでした)、論文が本ほどの大きさになるため、読みやすくなることはありません。

どの程度まで?

簡潔さ = パワーという考えを否定する人はたくさんいます。単に同じかそうでないかと議論するよりも、簡潔さ

パワーはどの程度かと問う方が有益だと思います。なぜなら、簡潔さは明らかに高水準言語の目的の大部分を占めているからです。それが高水準言語の目的のすべてではないとしたら、他に何の目的があり、これらの他の機能は相対的にどれほど重要なのでしょうか。

私は議論をより文明的なものにするためにこれを提案しているのではありません。私は本当に答えを知りたいのです。言語が簡潔すぎると、その言語自体の利益にならないことがあるのでしょうか?

私が最初に立てた仮説は、病的な例を除いて、簡潔さは力と同一であると考えることができるというものでした。私が言いたかったのは、誰かが設計するどんな言語でも、それらは同一であるだろうが、誰かがこの仮説を反証するために明示的に言語を設計したいのであれば、おそらくそれができるだろうということでした。実際のところ、私もそれについて確信はありません。

プログラムではなく言語

ここで明確にしておきたいのは、ここでは言語の簡潔さについて話しているのであって、個々のプログラムの簡潔さについて話しているのではないということです。個々のプログラムがあまりにも密集して書かれてしまう可能性は確かにあります。

これについてはOn Lispで書きました。複雑なマクロは、正当化するために、そのマクロ自体の長さを何倍も節約する必要があるかもしれません。複雑なマクロを書いて、それを使用するたびに 10 行のコードを節約でき、そのマクロ自体が 10 行のコードである場合、マクロを複数回使用すると、行数が実際に節約されます。しかし、マクロ定義は通常のコードよりも読みにくいため、それでもまだ悪い動きである可能性があります。マクロを 10 回または 20 回使用しないと、読みやすさが実際に改善されない可能性があります。

どの言語にもそのようなトレードオフがあるのは確かです (ただし、言語が強力になるほど、そのリスクも高くなると思います)。プログラマーなら誰でも、賢い人が疑わしいプログラミング トリックを使ってわずかに短くしたコードを見たことがあるはずです。

したがって、それについては議論の余地はありません。少なくとも、私からすると。個々のプログラムは確かに簡潔すぎることがあります。問題は、言語はそうできるかということです。言語は、全体的な読みやすさを犠牲にして、短い (要素数が多い) コードをプログラマーに書かせることができるでしょうか。

言語が簡潔すぎると想像しにくい理由の1つは、何かを表現するための非常にコンパクトな方法があれば、おそらくより長い方法もあるだろうということです。たとえば、マクロや高階関数を多く使用するLispプログラムが密集しすぎると感じた場合、必要に応じてPascalと同型のコードを書くことができます。Arcで階乗を高階関数の呼び出しとして表現したくない場合は、

 (rec zero 1 * 1-)

再帰的な定義を書くこともできます:

 (rfn fact (x) (if (zero x) 1 (* x (fact (1- x)))))

すぐには例が思い浮かびませんが、言語が簡潔すぎる可能性があるかどうかという疑問に興味があります。ぎこちなく理解しにくい方法でコードを書くことを強いる言語はありますか? 誰か例を持っている人がいたら、ぜひ見てみたいです。

(注意: 私が探しているのは、区切り文字を省略でき、すべてに 1 文字の名前が付いているため単に短いプログラムではなく、上で概説した「要素」の基準に従って非常に密度の高いプログラムです。)