いものやま。

雑多な知識の寄せ集め

「Lucid, the Dataflow Programming Language」を翻訳してみた。(その4)

前回の続き。

1.4 命令型アプローチの問題

近年、プログラミング言語に対する命令型のアプローチは、ますますひどい兆候を示している。
従来の「主流」のプログラミング言語は、これまで以上に複雑な作業に、そして、これまで以上に多くの人々に使用されている。
最初のフォン・ノイマン・マシン(1950年代初めに作られた)は、1秒当たりわずか数百回の演算(加算など)しか実行できなかった。
プログラムは通常1人のプログラマーの仕事であり、数十行以上の長さであれば大きなものと見なされた。

最新のマシンは、1秒間に数十万回の命令を実行できるようになった。
現在のソフトウェアシステムの中には、数百万のプログラマシステムアナリストの長年の労力がかけられ、数百万行のコードを含んでいるものもある。
フォン・ノイマンの言語とマシンの基本原則は1952年以来ーー少なくとも1960年にALGOLが導入されて以来ーー変わっていない。
1985年にFORTRANとEDSACの子孫が彼らの年齢を示していることは驚くことではない。

フォン・ノイマンのアプローチに何か問題があるという最も明白な兆候は、これらのマシンや言語、特に言語の複雑さの、爆発的・指数的な増加である。
今やアクティブで使われている命令型言語は文字通り何百もある。
もちろん、異なるアプリケーションで異なる言語が必要になるというのは予期されうることである。
しかし、これらの言語は、同じドメインのアプリケーションを意図していても、本質的に異なるアイデアを採用していたりする。
ALGOL、PL/I、ALGOL 68、PASCAL、BASICのような、多かれ少なかれ「汎用的」言語もある。
特に、これらの言語は、ほぼ同じアプリケーションを想定していて、同じ概念(変数と代入文)に基づいており、「マシンに依存しない」と主張している。
それにもかかわらず、それぞれは何かと独自の方法をとっていて、それぞれと互換性がない。
これらの言語の1つから別の言語へのプログラムの変換方法はない。

フォン・ノイマンのアプローチの気が狂うような複雑さは、個々の言語そのものの状態に反映されている。
より野心的な汎用型言語(特にPL/I)は、想定されうる巨大な性質を持っている。
PL/Iのような言語は、何千もの制限や制約を対象として、何千もの機能、オプション、デフォルトを提供している。
いずれの人もPL/Iのすべてを知るなんて想像も出来ず、その「マニュアル」は、電話帳が何冊も集まったようなものになっている。

多くの言語設計者が、言語的な「過度の」アプローチを拒否し、PASCALなどの控えめな言語を作り出した。
これらの言語はおおまか成功しているが、結局のところ、いくつかの重要な点で欠けていることが常に示されている。
簡素化のプロセスは遥か遠く、最初の一歩がいまだ進めていないように見える。
有用で、完全に自然で、明らかに無害な記述やプログラムというのを書こうと思っても、書けなかったりすることが常だ。
たとえば、PASCALでは、100個の数値の配列をソートするプログラムを作成し、200個の数値の配列をソートするプログラムを作成できるが、任意の長さの配列をソートする単一のプログラムを書くことは出来ない。

もちろん、これらの制限は容易に解消できる。
型システムのちょっとした緩和、配列の概念のシンプルな拡張、マルチプログラミングのための追加機能・・・そして非常に迅速な言語となると、もはや単純ではなくなってしまう。
一般的な傾向として、シンプルで、しかし制限がありすぎて便利ではない言語は、拡張が繰り返され、やがては使い物にならないような膨大な怪物になってしまうようだ。
ALGOLはALGOL 68になり、FORTRANPL/I(あるいはFORTRAN 77)に拡張され、今はPASCALからADAが生み出されている。
制限と一般性との黄金比を得るのは不可能に思われる。
言語設計者は自分たちが成功していると思っているのかもしれないが、四半世紀経った今もFORTRANCOBOLが依然として最も広く使われている言語である。

フォン・ノイマン言語に関する第2の問題は、正確な仕様が欠如していることである。
マニュアルは通常、構文(どんなプログラムが許可されているのか)は明確かつ詳細に示しているが、意味(プログラムが何をするのか)はおおよその非公式の扱いしか示していない。
徹底的に網羅された電話帳(のように分厚い)マニュアルは、何が起こるのかについて、最も一般的な場合について、しかも非公式な言葉でしか記述していない。
本当に複雑な問題(特に、異なる機能の相互作用を含む問題)は、コンパイラ依存であり、コンパイラをよく知っているその実装の「教祖(guru)」に訊くしかない。
プログラマーは、言語の振る舞いを確認するために、短い「実験的な」プログラムを書くことがよくある。
この状況はダイクストラ(1976、p.202)によって、うまく記述されている。

以来、いびつで、不明確で、それゆえ不安定なソフトウェアシステムが増殖していくのを私たちは目撃している。
多くのプログラマは、彼らの仕事で必要となるキッチリしたツールで作業するのではなく、何が起こるのかも分からないような曖昧で掴み所のない環境、辺獄で暮らしている。
このような残念な状況の下では、正しいプログラムというのは、それが正しいと証明されたものであってさえ、意味がなくなる。
このようなシステムの増殖がコンピューティングコミュニティの士気に対して何をもたらしているのかは、記述しきれたものではない。

フォン・ノイマン病の3番目の症状は、最初の2つの必然的な結果であるーーつまり、まったく信頼できない。
プログラムは動作しない!
何のエラーも含まない12行程度以上の命令型プログラムを書くことは、ほとんど不可能である。
これらの「バグ」は、見つけるのが非常に困難であることが判明していて、プログラマは、プログラムの設計やコーディングに費やすよりもはるかに多くの時間をプログラムの「デバッグ」に費やすことになる。
結果として、エラーのないソフトウェアは、非常に高コストになる。
大きなシステムでは、すべてのバグを取り除くことは事実上不可能である。
微妙なエラーのいくつかは、(例えば、プログラムのあまり使用されていない部分に)何年も潜伏していて、突然「生き生きと現れ」壊滅的な結果をもたらしたりする。
もちろん、どの言語を使っていようと、人間は常に間違いを犯すだろう。
しかし、誤りをよく引き起こしていると経験的に判っている機能(goto文、副作用、エイリアシング)は、命令型アプローチにとって自然に示されるものであり、それなしでは困難と言える。

最後に、私たちはフォン・ノイマン・システムの第4の弱点について言及する。
これは最近明らかになった問題なのだが、最も重大であると示されるかもしれない。
それは、フォン・ノイマン・システムは非効率的であるということである。

経験豊富なプログラマーは、この声明が信じがたいと思うだろう。
彼らは、言語が複雑で、仕様が甘く、エラーが起こりやすいとは認めるかもしれない。
しかし、非効率的だなんてことがあるのだろうか?

ハードウェアを最大限に活用できるようにするとしたら、プログラマーがマシンに近づくようにする他ない。
実際、マシンに接近する必要があるということが、フォン・ノイマン言語で問題が起こるすべての原因になっている。
シンプルさ、信頼性、使いやすさなどは、すべて効率性のために犠牲になっている。
しかし、それでフォン・ノイマン・システムが効率的でないのだとしたら、それらは一体なんだというのか?

しかし、非効率性の批判は、言語よりもマシンに向けられている。
フォン・ノイマン・マシンは、一度に1つの命令しか実行できないーー 与えられた問題のロジックを何千も並行して計算することができる場合、フォン・ノイマン・マシンは、原理的に可能な速度より数千倍遅い速度で計算を実行することになる。
命令型言語は、フォン・ノイマン・マシンを最大限に活用するのに適しているかもしれないが、それらは必然的にマシンの逐次性を反映してしまう。

Christopher Stracheyは、単純ではあるが目を引く、この効率性の例を指摘したーーそれは、行列の乗算である。
Landin(1966、p.166)の後で、彼は議論で以下を指摘した:

純粋な命令型言語に関して、1つ不都合なことは、あまりにも多くのシーケンスを指定しなければならないということである。
行列の乗算をしたい場合は、行列の次元が n \times nであるなら、 n個の立方乗算(cubed multiplication)を行う必要がある。
これを行うための通常の(すなわち、命令型)プログラムを書くならば、それらがすべて実行される正確なシーケンスを指定する必要がある。
実際には、適切なグループにそれらをまとめ上げる限り、乗算をどのような順序で行うかは重要ではない。

この欠点は、プロセッサが高価なハードウェアであり、マシンのコストの大部分を占める時代には、あまり重要ではないように見えた。
しかし今や、LSIにより、プロセッサは比較的安価になった。
何千もの処理ユニットを持つマシンを構築できないという技術的あるいは経済的な理由は存在しない。
問題は、これらのプロセッサの活動をどのように組織し、同じタスクを協調して行えるようにするかである。

公平であるためにいうと、フォン・ノイマン・マシンに対する私たちの批判は、実際には、コンピュータシステムが単一の醜いフォン・ノイマン・マシンを中心としているという、システムアーキテクチャへの特定の見方に対する批判である。
実際、フォン・ノイマン・マシンは、長い間、システムの有用な構成要素であることが分かるかもしれない。
例えば、ネットワーク内に多数の適度なサイズのフォン・ノイマン・マシンを接続して、同じプログラム上で協調することが出来る。
これは確かにフォン・ノイマンボトルネックを回避する方法の一つである。
もちろん、多くの研究者は、まったく異なる方法でデータを処理する、まったく新しいマシンアーキテクチャ(データフローやリダクションマシンなど)に取り組んでいる。
とにかく、「フォン・ノイマン言語」を批判するとき、私たちが批判しているのは、単一のフォン・ノイマン・マシンで行われる計算の視点に基づいている従来の逐次・命令型言語である。

もちろん、原理的には、FORTRANのような命令型言語を何らかの並行(parallel)または分散(distributed)アーキテクチャで実装できない理由はない。
しかし、実装が単一の従来の逐次マシンのステップをシミュレートしただけであれば、そうすることには意味がない。
「パラレル」マシンの計算能力の大部分はアイドル状態になる。
FORTRANの本当に価値ある(言い換えれば)データフローの実装には、隠された並列性を検出するための洗練されたプログラム分析の段階が必要となる。
しかし、私たちは、プログラマと(コンパイラの)実装が交差する目的で働くばかばかしい状況にいることに気付くだろう。
この言語は、プログラマアルゴリズムを厳密に連続した形でコード化するように強制する。
一方、(コンパイラの)実装はこのコードを「クラック」して、元の並列アルゴリズムを復元する必要がある。
当然のことながら、命令型言語に「並列性」のための機能を追加することによって、この最後の問題を解決しようとすることになる。
しかし、結果として得られるプログラムはさらに複雑になり、新しい機能と言語の他の側面との相互作用は、前述の問題を悪化させるだけである。

レイノルズ(1970年)はかつて、言語設計者が直面している課題は、シンプルで汎用的かつ効率的である(すなわち、効率的なプログラムを書くのがかなり簡単である)ことを同時に満たす言語を生み出すことであると言った。
従来の命令型の研究の設計者は、これまでのところ、この挑戦に応えられていなかった。
たとえば、PL/Iは汎用的で効率的だが、シンプルではない。
PASCALはシンプルで効率的だが、特定の重要な側面では十分汎用的であるとは言えない。
APLはシンプルで汎用的だが、効率的ではない。
最後に、ADAはシンプルではなく、それが汎用的、効率的であるかはこれからといったところだ。

1.5 いくつかの示唆された解決策

命令型言語の最も熱心な支持者でさえ、何かが非常に間違っていると認めるだろう。
しかし、「ソフトウェアの危機」と「並列性の問題」については、いくつかの異なった態度がある。
ここではいくつかの(いくらか滑稽された)よくある視点を紹介する。

まず、私たちがクリント・イーストウッドの見解と呼ぶものがある。
この「険しいウェスタン」の観点では、プログラミングは厳しくて困難な作業であり、常にそうだが、しかし、「プログラマプログラマーがやらなきゃいけないことをやらないといけない」としている。
こういった「カウボーイ」達によると、実際の問題は、仕事を終わらせるのに十分なほど賢明でタフな本物のプログラマの不足していることである、という。
カウボーイ達は、長い時間「鞍(すなわちターミナル)で」プログラムを学び、他のすべての人が同じことをするのを期待している。
誰かがミスをするなら、それは「ミスした人自身の問題」である。
カウボーイ達は、一般的に「本での学習」(理論)と「新鮮な」言語を信じていない。
彼らはPL/Iが大きすぎる、あるいは、Cが低レベルすぎると不平を言う「未熟者」達を軽蔑する。
彼らにとって、PL/Iは機能が多すぎるなんてことはないし、Cは汚れきってなんかいない。
彼らが望むのは、コンピュータにもっと近づき、それを「ダンス」させるための、もっと強力なツールである。

カウボーイ達は確かに賞賛に値する。
彼らの中でも最も優れたもの(真のストレートシューター)は、普通のプログラマーをしても「臆病者」であるという印象を与えるようなソフトウェアを作り出すことが出来る。
しかしながら、そんな本当にタフなカウボーイ達が身のまわりにいるのかということを自問しなければならない。
実際にはそんなことはほとんどなく、ほとんどのソフトウェアは、ターミナルのワイアット・アープではなく、普通の、謙虚なトレイル・ハンド達によって書かれるという事実に従うしかない。

次は、私たちが「ミスター・ウィザード」学園の教えと呼ぶものである。
ウィザード達は多くの点でカウボーイ達の対極にいるが、プログラミングとプログラミング言語は本質的に非常に複雑であるということだけに関しては、彼らは意見を同じにしている。
ウィザード達の見解では、問題は、これらの言語の理論的・数学的な理解が不十分なことである。
ウィザード達は、古の伝承にある埃にまみれた多くの研究を調べている。
彼らはラムダ(Lambda)の"the Arts Magical, the Logyck Symbolyck and the Calculus"(訳せない・・・)を学んでいる。
彼らはタルスキ(Tarski)、チャーチ(Church)、カリー(Curry)の偉大な名前を思い起こさせる。

ウィザード達の究極の夢は正式なプログラム検証のためのシステムである。
これにより、言語やプログラムがどれほど悪くあろうとも、プログラマは(ウィザードに助けを得て)与えられたプログラムが正しいという気密な証明を生成することが出来るようになるだろう。
したがって、プログラム検証は、元のプログラムを金に変える賢者の石の一種である。
ジョン・マッカーシー(John McCarthy、1965、p.219)は、以下のように言っている:

合理的・数学的な計算理論が開発されれば、デバッグの排除という賞金を獲得することが出来る。
これまでとは違って、プログラマは、プログラムが希望した性質を持っていることをコンピュータで確認するための証拠を提示することになるだろう。

問題は、ウィザード達が手腕を振るったとしても、フォン・ノイマン言語の基本的な性質はもちろん変わらないということにある。
ウィザード達は正式な仕様書の作成に成功したかもしれないが、これらの仕様書で、理解不能なラムダ計算の電話帳(マニュアル)の分厚さも増す。
同様に、「実際の」プログラムの正しさの証明も、実用的ではないことが判明している。
ウィザード達は、「汚れた」機能(副作用、goto文、エイリアシング)でトラブルに遭遇した。
もちろん、これらはまさにカウボーイ達が最も愛している機能である。

しかし、カウボーイ達やウィザード達の有様を独善的な侮蔑で見ている、第三のグループがある。
このグループのメンバーは野性的な狂信者であり、構造化プログラミングの福音の伝道師達である。

伝道師達は、コンピュータサイエンス版の原罪の教義を認めている。
彼らは、人間が、注意散漫でミスをおかし(怠惰の罪(the sin of Sloth))、死んだ心の貧弱な力を超えた任務を遂行する(傲慢の罪(the sin of Pride))という固有の傾向を持って生まれてくると信じている。
罪の贖いとは、もちろんバグである。
バグ(および一般的なソフトウェアの問題)を回避するには、プログラマは悪の行いを放棄し、善なる行いを採用しなければならない。
プログラマは、儀式によってソフトウェアが悪霊に憑かれるのを防ぐために、プログラミングの厳格な規律(方法論)を採択するよう奨励される。

構造化プログラミングの弟子たちは、実際、悪いソフトウェアとの戦いで重要な成功を収めている。
それにもかかわらず、男と女の心の中に潜む邪悪は、予想以上に征服するのが難しいと分かっている。
節制ーーつまり、プログラマを邪悪に誘惑する禁断のリンゴである、goto文やポインタ変数といった機能を控えることは、伝道師達の教えにおいて重要な役割を果たしている。
しかし、既に気付いているように、プログラマはこれらの禁断の果物を避けがたい。
伝道師達は、すべての邪悪な特徴が追放された純粋で聖なる言語を夢見ている。
しかし、実際には、これらの「構造化プログラミング」言語の設計者は常に、効率の観点からその原則を妥協することを余儀なくされている。
したがって、忠実な人たちはひどく避けがたいジレンマに直面するーーすなわち、道徳に従って、正しいが効率的ではないプログラムを書くか、不道徳になり、効率的だがバグに苛まれたプログラムを作るか。
伝道師達は、自問しなければならないだろう。
「なぜ、悪魔は速いプログラムを持っているのだろうか?」と。

しかし、人間の能力は低いとする伝道師達と同じ意見を持つが、より現実的な解決法を強調する、第4の、より楽観的な視点がある。
私たちは彼らを、「技術者」学園の考えと呼んでいる。

技術者(Boffin)達は、人間の弱さがソフトウェア危機の根本的な原因であることに同意していて、なので、人間は裸で非武装のままではソフトウェアを生産することが出来ないとしている。
しかし、技術者達が提供する武装は、物理的、機械的なものであり、道徳や精神的なものだったりはしない。
彼らの目標は、構造化されたエディタ、診断的なコンパイラ、賢い型検査、洗練されたデバッグシステムといった、強力なプログラミングツールをプログラマに提供することである。
彼らの目的は、バグと同等の条件で戦える、技術的に強化されたスーパーヒーロー、超人的なプログラマを生み出すことである。

もちろん、「プログラマのワークベンチ」にある巧妙なデバイスが非常に有用であることは否定できない。
問題は、それらはプログラマの強みはもちろん、弱点も増幅することにある。
百万ドルのプログラマの役立つベルトにあるガジェットは、バグが多発する膨大なプログラムを簡単に作成できるようにして、膨大な量のリソースをデバッグに当てさせることが出来るようになる。
新世代のスーパープログラマによって作られたスーパープログラムは、恐ろしいスーパーバグとの新しい競争を生み出すのだろうか?
結果として、プログラマはさらに強力なツールを必要とするのではないか?
これらのツールはさらに恐ろしいバグを生み出すのではないだろうか?
そのプロセスは、終わることを知らない!

対照的に、私たちが「自前主義」または「修正主義」学園と呼んでいる野心は、かなり控えめである。
この観点では、ソフトウェアの危機は、命令型言語の重大な欠陥の結果であるとしている。
しかし、これらの欠陥は、フォン・ノイマンのアプローチ全体に内在する弱点の結果ではなく、むしろ設計ミスの産物である、と。
必要なのは、ちょっとした工夫、ちょっとした新しい部品、そして厄介な機能のいくつかの削除で、それですべてがうまくいくだろう、と。

今や、命令型言語が改善され、かなり単純な変更が大きな利益をもたらすことが多いことは間違いない。
問題は、ある変化が別の変化につながることであり、ある領域の改善が別の領域に悲惨な結果をもたらす可能性があるということである。
我たちは、ALGOLとFORTRANの修正がいつも成功しているわけではないということを見てきている。
これは特に、「並行性」や「データフロー」の機能を使用しようとした場合に当てはまる。


ということで、命令型言語の問題に関する話と、それに対する様々な態度の話。

正直、1番目と2番目(およびそれによる3番目)の話は、今となっては割とどうでもいい話だけど(ダメな言語は駆逐されてきたし、意味論もそんなに話題になることはない)、重要なのが4番目の指摘。
つまり、逐次的な記述なので、並列プログラミングが非常に難しいということ。

関数型が最近もてはやされるようになってきたのは、一つのプロセッサでの処理速度に限界がきて、マルチコア化が進んできた結果、この問題が浮上してきたからなのだけど、それを70年代後半〜80年代前半のこの時期に指摘しているというのは、非常に興味深い。

そして、それに対する様々な態度というのも、面白い。

カウボーイというのは、スーパーハカーになりゃいいじゃん、という態度だし、ウィザードの態度は、最近の関数型の信奉者の姿に非常に近い。
伝道師の、人は間違えるものだから、バグが起こりにくいように決まりを作って順守させようというのは、各種コーディング規約や、組込みでのMISRA-Cを思い起こさせるし、技術者の主張は、今やIDEなしではプログラミングが大変というのを予言しているようにも見えたり。

この本、書かれてから30年以上も経ってるのにね。
なんという先見の明か・・・

今日はここまで!