はじめに
ここ最近、関数型言語などと言われているものに関する怪情報が qiita 上に局地的に現れ、それによってグーグル先生の検索結果も汚染されているため、迷える子羊のような FPL1 初学者がクノッソスの地下に投げ込まれるという悲劇を生んでいることは、賢明な諸兄におかれましては承知の事実かと思います。このような珍事は、是非とも qiita の運営が何とかするべきなのではないかと愚考するところであります。関数型というワード自体にアレルギー反応を示す qiita 購読者を増やすことは、きっと運営諸氏にとっても本意ではないことでしょうし。まあ、どうでも、いいですけど。
そんな中、最近も義侠心にあふれた @hiruberuto ニキがツッコミを入れておられました。(see http://qiita.com/hiruberuto/items/26a813ab2b188ca39019 ) さすが世紀の数学者と讃えられた漢です。無限の次元に轟くその名は伊達ではありません。ただご覧のように、案の定星の導き手によりそのコメ欄は混沌の渦に投げ込まれております。
不肖私めはその時、シンデレラガールズの1話をヘビロテしており、無駄にハイになっていた結果、如何にこの幸せを分け与えんとわさわさしておりました。そこで、たまたま見つけた、今回の混沌の宴の被害者であるところの @potimarimo 氏へ助け舟をお出しすることにしました。しかし、所詮はスタドリでラリっている脳髄、上手にお答えをすることは出来なかったようです。今現在 @hiruberuto ニキがカンカンでいらっしゃる以上、コメ欄を伸ばすのも憚られますし、多少長くもなるでしょうから、一筆記事を書かせていただき、@potimarimo 氏へのリプの責任を取ろうと思います。
このような理由で、本来はチラ裏か益田あたりに放り込むべきポエムを無関係の諸兄にお晒しすること、切にご容赦願います。なんでもしますから。あとやっぱりふわっとした話になります。厳密な話を知りたい時は図書館に行った方がいい。
歴史の話
ここでは、体系的にプログラミングを学ぶときにはおおよそ学ぶであろう、プログラム言語の歴史をちょっと思い起こしていこうと思います。とは言っても、別に多く語ることはありません。初めに機械語があり、ニーモニックが与えられ、FORTRAN, COBOL, C が生まれ、それからなんだかんだあって C++ だの Java だのその他魑魅魍魎が生まれたという、なんでもないみんな知ってる話です。
プログラム内蔵方式の計算機が使われる様になってまもなく、機械語をわかりやすく表現するためのニーモニックが登場します。もちろんアセンブルは手で行ってました。そんな時、フォン・ノイマンが、アセンブルを行う機械をつくろうとしてた人に「そんな幼稚園児にでも片手間にできることをするために新しい機械を設計するとかバッカじゃねーの?(ハナホジー」といったとか言わなかったとかいう話があるようです。しかし、残念ながらすべての人がノイマンのようなぶっ壊れ人外計算マッシーンなわけではありませんから、アセンブラマシンはどうしても必要でした。
更に、計算機で行わなければならない仕事が指数関数的に増えていくと、当然のようにコード量も同じように増えていきます。これを保守するには、ニーモニックよりわかりやすい何かが必要だったので、FORTRAN のような逐語的にニーモニックに翻訳できる様な言語が開発されます。逐語的に訳していくわけですから、基本的な構造はニーモニックのそれと同じ、つまりにコンピュータにさせる仕事を順番に記述していく、手続き的なものになりました。
さて、時が下ると、「FORTRAN の F*** な仕様のせいでロケットが落ちた」とかいう噂2がまことしやかに囁かれるようになります。別にそのせいじゃありませんが、今の言語ではまだ分かりにくい使いにくい、と考えた多くの人は、もっとわかりやすく書きやすく保守しやすい言語を作るべく、言語の改良を行います。その中で、サブルーチンだの関数だの配列だの何だのが生まれ、構造化だのオブジェクト指向だのが生まれていくわけです。
この過程を乱暴にふわっと言うと、「機械語という普通の人には簡単に扱えないものを、扱いやすい表現に変えていくための歴史」ということができるでしょう。これは実に自然な動きですね。ただ、多くのケースにおいて、手続き的に動作を記述していく、という伝統は受け継がれてきました。これは習得すれば実に使いやすい概念だったからでしょう。オブジェクト指向言語と手続き型言語を分けるべきなのかどうか、ということは僕は知りませんが、基本的にオブジェクト指向言語は局所的には手続き的です。手続きの記述という手法を極めていくと、人々は OOP に辿り着いたわけです。(この物言いも乱暴ですかね)3
ちょっと違う歴史の話
さて、プログラミング言語の歴史の話をするときには、もうひとつ、重要な話をする必要があります。別の文脈、つまり理論の世界の話です。僕は数学の専門家ではないので、あまり厳密な話はできません。詳しく知りたい方はぜひ文献にあたって下さい。
その昔、ギリシャとかにいた、暇つぶしに哲学してたら遊びじゃなくなってしまった人たちが、人間の思考、いわゆる論理を体系化することを考え始めました。まあそういったことはいろんな文化圏で行われてきたようですが、アリストテレスの三段論法は有名ですね。
時代が下ると、ある人達は、論理を記号で書くと都合が良いことに気づくようになります。記号を使うことによって、解釈の余地なく思考を伝えることができる、ということですね。特に数学の記述にこれは最適でした。そうすると、どのような記号の使い方をするのが最善かという研究、もうちょっとマシな言い方をすれば、記号論理の体系とその性質についての研究が始まりました。このようにして数学で数学を調べるメタ数学、いわゆる数学基礎論が芽生えます。
さて、集合論に関するパラドクスや、形式 vs 直感 vs 論理 三つ巴の仁義無き戦いなどいくつかの試練によって、この分野は急速に洗練されていきます。20世紀の初め頃、当時の数学者のドンであったダフィット・ヒルベルトは、「もうこれで数学は制覇されたも同然じゃね?」と考えるようになり、ヒルベルト・プログラムと呼ばれる一大プロジェクトをぶちあげます。これは「有限の立場で数学の完全性と無矛盾性を証明する、またそのような体系を構築する」といったものでした。ごく荒く言うならば、今見てきたような、厳密な記号による論理体系によって数学を完全に記述しきるといったものでしょうか。
さて、このヒルベルト先生の壮大な野望は、クルト・ゲーデルという神経質な院生によって大ダメージを受けます。有名なゲーデルの不完全性定理です。それが何を成したか、などということは今回は全く関係がなく、重要なのはそこに出てくる論理体系です。彼はとある述語論理体系の上に「その体系自身」を定義する事により、その証明を行います。4
その恐るべき証明に世間がざわついていた頃、アラン・チューリングは、ゲーデルの証明と同等のことを「機械」を使って行う事ができるのでは、と考えます。そのようにして、チューリング機械という仮想的な機械を記述することに成功しました。少なくとも、このチューリングの論文のサブジェクトは、コンピュータを作ることではなく、不完全定理の別回答を与えることだったのです。5
ストレンジラブさんことフォン・ノイマン6は、ENIAC や EDVAC といった黎明期の計算機のプロジェクトをちら見して、一瞬でこれはチューリング機械っぽくね?ということに気が付いたようです。プログラム内蔵式の報告書を自分の名前で提出し、現在に至る計算機の礎を築きます。7 ここで前節にリンクしましたね!
更に、チャーチという人は、チューリング機械やゲーデルの体系と同等の仕事をする、別の体系を考え出します。これがラムダ計算と呼ばれるもので、だいたい FPL と呼ばれる言語のご先祖になります。
このように、全く異なる公理により定義されたいくつかの体系が、同等の計算能力を持つことが明らかになりました。その様な体系をチューリング完全な体系と呼んでいるわけです。
さて、ここまで見てくると、次のことに気が付きます。つまり、前節の機械語と格闘していた時代に、既に人類はヒューマンフレンドリーな言語を手にしていたのではないか、と。
非手続き的な言語の悲哀
ゲーデルの論文の体系、いわゆる述語論理は、数学で行う証明の書き方をしていましたから、少なくともそれに慣れている人にはわかりやすいものでした。これは、論理の記述という数世紀に及ぶチャレンジの大きな果実であり、思考の表現として適しているのはある意味当然です。そして、それはプログラム言語そのものでした。実際にほぼ同等の言語として prolog があります。また、ラムダ計算を元に、早速 Lisp が開発され、これはその時点で既にかなり完成されたものでした。
ですから、人類が計算機を手にした時には、人類は既に自らの思考の型に適したコンピュータ言語を手に入れていました。手続き型の言語が、人ならざるものを人の形に年月をかけて近づけていたのに対して、数理的なアプローチの言語は最初から人の形を与え、それを研ぎ澄ませていたのです。
じゃあなんで後者がメインストリームに乗らなかったのか。それは簡単ですね。機械にアセンブルさせることすらどうかといっていた時代に、機械の動きと全く違う表現をする言語を動かすのはナンセンスです。それは綺麗事であり、実務的ではありませんでした。Lisp 専用機とかあったらしいですけどね。あまり知られてないですよね。
しかし、言語研究者は研究室にこもって、数学者のおもちゃなどと言われながら数理論理的なアプローチでの研究を続けます。言語開発者はそれが理想の形であることを知っていますから、時々研究の結果がプロメテウスの火のように手続き的な言語に与えられることになります。型、函数、リストや木構造、イテレータ、テンプレート/ジェネリクス、無名関数、型推論…… これは、手続き的な言語が、そのあるべき形に回帰しているかのようにも見えます。
手続き的な言語と数理論理的な言語8の違い
例えば、たかしくんがお母さんからお金をもらい、いくつか買い物をしてお釣りをもらってきたとします。この時、おはじきやコインを取り出して並べながら解くのが手続き的な解法であり、方程式を立てて求めるのが数理論理的な解法と言えます。おはじきを並べても人に伝えるのは難しいですから、おはじきの並べ方や動かし方をうまく言語化して研ぎ澄ましてきた、というのが手続き的な言語の歴史でした。9
ここで「手続きを馬鹿にしてる」と怒らないで下さい。手続き的なアプローチは必要です。まず、機械はそのやり方しか知りません。ですから機械に寄った低レベルなところに触れるときには、どうしてもこれが必要になります。機械の限界に挑まなければならない時にも、やはり手続きに頼る必要があるでしょう。また、レイヤーが違いますが、例えば、工学で定積分を行う場合、扱う函数は一般には解析的ではありませんから、おはじきを並べるように短冊やメッシュに区切って数え上げることになります。特に工学の分野では、このように「力技」が求められる事が多くあり、それには手続き的な発想が求められると思います。10
しかし、一般的に考えて、式を立てる方がスマートじゃありませんか?そちらのほうが問題の本質を表してはいないでしょうか。式を投げれば、数学的に証明されたプログラムが自動的に答えを出してくれるのです。我々は、式を建てることにのみ注力すればよく、機械の中で弾かれるおはじきを気にすることはないのです。
まとめ
例えば、等加速度運動をする二次元の剛体の運動は、(今はどうなってるか知りませんが)高校の物理で習う、微積を使わない公式で求めることが出来ます。しかし、一般的な運動方程式から求める方法を知らなければ、本質を理解しているとは言えないかもしれません。
プログラミングにおいても、手続き的な手法を学べば十分にそれを行うことが出来ます。しかし、その基盤である数理論理的な部分を知らなければ、その本質を理解しているとは言えない、かもしれません。例えば集合論、整数論、群や体や順序、命題論理やラムダ計算。数理論理的な言語は、こういった理論的なサムシングと実務とを実に簡単に橋渡ししてくれます。そして、それは単なるディレッタントの楽しみではなく、極めて実用的なツールであり、そこら辺の説明はそれぞれの言語の宣教者が嬉々として行ってくれるでしょう。それらは万能ではありませんが、貴方のやりたいことは大方できるはずです。
だから、別にその言語は悪い言語じゃないですし、そのパラダイムは悪いものではありません。ただ、こっちを覗いてみてもバチは当たらないんじゃないか、と思います。
おわりに
- 森久保ぉ!ポエムが長すぎるぞ!
- 今晩デレマス2話の放送があるからこんなの読んでないでみんな見ましょうね〜
- これは捨て垢です(迫真
-
関数型プログラミング言語の略。こう言わないと言語学者の鉄槌を受けるかもしれないしそうじゃないかもしれない。 ↩
-
都市伝説らしいが、噂の火種はあったとか。 ↩
-
厳密には OOP はそのような文脈で生まれたわけではないらしいですが、実際の応用としては、手続き型の言語をブラッシュアップするための手法として用いられているのは間違いないでしょう。 ↩
-
この辺までの話は、ひどくざっくり話しています。ぜひ不完全定理に関する数学の解説書を当たってください。個人的には http://www.amazon.co.jp/dp/4875251068 らへんが好きですが、大人気の結城先生の本でもいいでしょう。なんてたって可愛い女の子が出てきますし。 ↩
-
チューリングの論文はネットに転がっています。僕でも斜め読みできるくらいですから、皆さんにも簡単に読めるかと。 ↩
-
僕は真っ先にこの人を思い浮かべます。 ↩
-
パクリ説もあり。 ↩
-
この言い方もわかりにくくてどうかと思いますけどね。 ↩
-
念の為に付け加えますが、俺の言語ではその式を直接書いて答えを出せるぞなどとは言わないで下さい。これは問題を単純化した例です。貴方の入力した式は、言語システムやプロセッサがもっと原子的な手続きに分解しています。 ↩
-
もちろん、その実装を例えば関数プログラム的に行うことも出来ます。でも、大局的には手続き的ですよね。 ↩