Pascalが私の好きな言語でない理由
1981年4月2日 Brian W. Kernighan
要約
プログラミング言語Pascalは、コンピュータサイエンス教育における授業の主要な言語となっている。また、その後の言語開発に強い影響を与えている(特に、Ada)。
Pascalは元々主として教育用言語として意図されたが、シリアスなプログラミング(例えば、システムプログラミング作業やオペレーティングシステムさえも)のためにも益々推奨されている。
Pascalは、少なくともその標準の形は平凡であり、シリアスなプログラミングに向いていない。この論文は、その理由の幾つかの私個人の発見を議論する。
1. 起源
この論文は2つのイベントに起源を持つ。すなわち、CとPascalの比較についての論文(1, 2, 3, 4)が次々に発表されていることと、'Software Tools'(5)をPascalで書き直す私個人の試みだ。
CとPascalの比較は、殆どLearjet(訳注:ビジネスジェット機)とPiper Cub(訳注:軽量プロペラ機)の比較に似ている。一方は何かをさせるため、他方は学習のため。従って、そのような比較は幾分非現実な趣がある。だが、'Software Tools'の改訂版は更に的を得た比較だと思われる。その中で、プログラムは元々Ratfor(プリプロセッサで実装された、Fortranの「構造的」方言)で書かれた。Ratforは実は変装したFortranだから、Pascalがもたらす重要なもの、つまり文字処理にもっと適したデータタイプ、一般の人のデータ編成をうまく定義するためのデータ構造化能力、データについて真実を語る強い型付け、を持っていない。
Pascalでプログラムを書き直すつもりよりも、もっと困難であることが判明した。この論文は、プログラミングのためのPascalの適合(プログラミングのための学習と区別して)について経験した教訓を引出す試みである。それは、Pascalと、C又はRatforの比較ではない。
プログラムは最初、バークレイのカリフォルニア大学で開発されたPascalイン� �ープリタpiによりサポートされたPascal方言で書かれた。その言語は、JensenとWirthの名目上の標準(6)に近く、良い原因診断と注意深いランタイムチェックが付いている。それ以降、プログラムは4つの他のシステムでも走り続け、プリミティブな新しいライブラリを除いて変更は無い。4つの他のシステムとは、アムステルダム自由大学(以下、Vrije Universiteitを意味する、VUとして引用する)からのインタープリタ、バークレイシステムのVAX版(本当のコンパイラ)、Whitesmiths, Ltd.から提供されているコンパイラ、Z80上のUCSD Pascalである。最後を除いて、これらのPascalシステムはすべてCで書かれている。
Pascalは議論の多い言語である。最近の参考文献表(7)は"議論、解析、討論"の表題の許に175項目をリストしている。最も屡々引用される論文(非常に読む価値がある)は、Habermann(8)による強い批判、LecarmeとDesjardinss(9) による同じく強い返答だ。BoomとDeJong(10)による論文も良い読み物だ。Wirth自身のPascalの評価は[11]で見つかる。私は文献を総括する意欲も能力も無い。この論文は私個人の観察を記録し、その大部分は必ず他の人の見解と重なる。私は、以下の問題点周辺に題材を編成する。
・型とスコープ
・コントロールフロー
・環境
・コスメティック
及び、多少重要度が下がるエリアについて。
始めから私の結論を述べる。初心者にプログラムの方法を教えるためにはPascalは立派な言語かも知れない。それについて私は直接の経験を持っていない。1968年では相当な業績だった。最近の言語設計に確かに影響を与え、その内でもAdaが最も重要だろう。しかし、その標準形式(現行と提案済みの両方)の中で、実際のプログラムを書く� �めにはPascalは適当ではない。環境とトリビアルなやり取りをし、他の誰かによって書かれたプログラムを使用しない、小さな自給自足プログラムだけのために適している。
2. 型とスコープ
Pascalは(ほぼ)強い型付け言語だ。大雑把に言えば、プログラムの各オブジェクトは、オブジェクトの正当な値とオブジェクト上のオペレーションを暗黙に定義する、上手に定義された型を持つことを意味する。コンパイルとランタイムチェックの或る混合によって、言語は不当な値とオペレーションを禁じることを保証する。勿論、コンパイラは言語定義の中で暗黙に示されたチェックの全てを実際にはしないかも知れない。更に、強い型付けは次元解析で混乱しない。人が以下のように'apple'と'orange'を定義するならば、
type
apple = integer;
orange = integer;
appleとorangeを伴なう任意の勝手な算術式は完全に正当である。
強い型付けはいろいろな方法で現れる。例えば、関数とプロシージャへの引数は正式な型マップのためにチェックされる。整数を期待するサブルーチンに浮動小数点数を渡す、Fortranの自由は消えている。これは、確実にエラーを引き起す指示を警告するのだから、Pascalの望ましい特質であると私は考える。
整数型変数は正当な値の範囲を持つと宣言されるかも知れない。コンパイラとランタイムサポートは、小さなものしか保持しない変数に大きな整数を置かないことを保証する。勿論ランタイムチェックはかなりな科料を払うけれども、これもまたサービスのようであるらしい。
型とスコープの幾つかの問題に移ろう。
2.1. 配列のサイズはその型の一部である
人が以下のように宣言するなら、
var arr10 : array [1..10] of integer;
arr20 : array [1..20] of integer;
arr10とarr20は各々10と20の整数の配列だ。整数配列をソートするためのプロシージャ'sort'を書きたいとしよう。arr10とarr20は違う型を持っているので、両方をソートする単一のプロシージャを書くことは可能ではない。
これが特に'Software Tools'、そして一般的にプログラムに影響すると私が思う処は、ソーティングのような共通的、一般的な目的のオペレーションをするためのルーティンのライブラリを作るのを実に困難にしていることだ。
最も常に影響を受ける特別なデータタイプは'文字の配列'で、Pascalでは文字列は文字の配列だからである。文字列aの中で文字cが最初に現れる位置又は無ければ0を返す関数'index(s,c)'を書くことしよう。'index'の文字列引数を処理する方法が問題だ。文字列の長さが違うので、'index('hello',c)'と'index('goodbye',c)'の呼出し両方を正当には出来ない(出来ないのだから、'hello'のような固定文字列の終りをどうやって感知するかの疑問は省く)。次に試みることは、
var temp : array [1..10] of char;
temp := 'hello';
n := index(temp,c);
しかし、'hello'と'temp'は違う長さなので、'temp'への代入は不当だ。
この無限の後退から抜け出す唯一つの手段は、各可能な文字列の長さのためのメンバーを持つルーティンのファミリーを定義することか又は、文字列すべて('define'のような固定文字列を含む)同じ長さにすることである。
後者のアプローチは、2つの大きな邪悪のうち、ましな方だ。'Software Tools'では、'string'と呼ばれるデータタイプは、
type string = array [1..MAXSTR] of char;
として宣言され、ここで定数'MAXSTR'は"十分に大きく"、そして、全てのプログラムの全ての文字列は正確にこのサイズだ。プログラムを走らせることが可能だけれども、これは理想からは程遠い。便利なルーティンの本当のライブラリを作る問題の解決にならない。
固定サイズの配列表現が簡単には受入れられない幾つかの状況がある。例えば、テキストの行をソートするための'Software Tools'プログラムは、収容出来るだけ多くの行を持つメモリを満杯にすることによって作動する。その動作時間は、どのようにメモリを目一杯にパックするかに依存する。
従って、'sort'のためには別の表現が使われる。文字の長い配列と、この配列へのインデクスのセットである。
type charbuf = array [1..MAXBUF] of char;
charindex = array [1..MAXINDEX] of 0..MAXBUF;