はじめに
関数プログラミング、とくに、Haskellの背後にある考え方について考えてみるポエムです。
普段はあまり主観を書かないのですが、年初は何か特別な気分にしてくれますね。 Qiitaなのでポエムでも、まぁよし。
この内容は、個人主観であって、Haskellや関数プログラミングのコミュニティにおける共通見解や代表意見ではありません。
関数プログラミングとは何か
"関数プログラミングとは何か"、これは、色々な人が色々な解釈を持っていますね。
丁寧な説明記事が沢山ありますが、山本さんが語られていたように、"関数プログラミング"に対して抱くイメージは人それぞれかと思います。
コンピューティングが直面している問題との関係
半導体の微細プロセスでは、そろそろ原子1つの大きさが影響し始める領域に突入しつつあります。それによって、単体CPUの周波数性能の向上が頭打ちになりました。つまり、ソフトを書けば、ハードが自動で性能を上げられる時期はとうに過ぎ去ってしまいました。並行/並列処理や分散処理は不可欠であり、難易度は上がる一方です。
さらに、各々のソフトウェアの記述量は膨大になり、開発期間や品質の面でも、大きな問題を抱える状況となってしまいました。
そこで何とかそれらの問題への対策を探したいわけですが、そんなときはやはり、先人の知恵を拝借するのが一番よろしいですね。巨人の肩に立ってみましょう、と色々と考えられたのではないかと推察します。
有史以来、人類が探求し構築してきた最も強力な道具の一つ、そう、「数学」の力を借りれば良い、のではないかと。
関数プログラミングの背景にあるもの
個人主観ですが、このように、"関数プログラミング"は、コンピューティングにかかわる問題に対して、先人の知恵である「数学」の力を借りることによって挑もう、というアプローチではないかと思っています。
つまり、"関数プログラミング"は、小さなテクニックや流行のことではなく、
コンピューティングの課題に対して、「数学」の力を借りて挑もう、という大きな流れ、ムーブメント
の一つではないかと思います。
「関数プログラミング入門(いわゆるIFPHの第二版)、Richard Bird」のまえがきには、私のお気に入りの一節があります。
本書の主たる目的は、プログラミングを数学的な活動とみなす考え方を伝えることにあり、、、
おそらくこれが、関数プログラミングをサポートする言語系の開発を駆動している思想の一つではないかと推測しています。
なので、純粋性や副作用、評価戦略や云々、、、などのいわゆる関数プログラミングの特徴は、「数学」の力を借りる上での表層的なものの一つであって絶対的なものではないのであろうと思います。
例えば、純粋性はあくまで、コンピュータ上のコード列を数学的な関数のように扱い、数学上の道具を適用可能にするためのお作法、と行った感じです。
現実は甘くない
しかしながら現実は甘くなく、抽象度の高い数学と、現代の主流のコンピュータ(CPU)との間には大きなギャップがあります。
現代のCPUは、プログラムカウンタで示される逐次命令流により、物理的なメモリの内容を書き換えることによって計算を進めるモデルに基づいて設計されています。
これまでのCPUは、そのようなモデルを前提としたC言語を中心とした言語系を、高速化する最適化アプローチにより設計されてきています。CPUとC言語系は、開発において相互に強い関係を持って発展してきました。
このCPU的な実行モデルに対しても、数学の様々な道具を使うことは可能であろうと考えられます。
しかしながら、例えば、数学の基本単位の一つである「関数」を、CPU上に安全に実装するという単純な事さえ容易ではありません。
例えば、「同じ引数で関数を実行すれば、いつでも同じ結果が得られる」という単純な特徴を確実に実装することは、案外、容易ではありません。
数学的な関数に近い特徴さえ実装できないということは、数学的な手法を適用しにくいということになります。
Haskellのアプローチ
Haskellは、関数プログラミングの特徴をサポートする多くの言語仕様の一つです。
Haskellの言語仕様では、一例として、コード上の関数を、数学的な関数の特徴に近づけることに挑戦しています。
具体的には、例えば、「同じ引数で関数を実行すれば、いつも同じ結果が得られる」ことを、保証するための仕組みを言語仕様レベルで導入しています。
他にも、型の概念を活かして、様々なチェックを静的に行うとともに、抽象度の高い表現を実現するという挑戦も行われています。
一方で、それらを実現するために背負っている代償もあります。どの特徴を代償と考えるかは人それぞれです。
GHCのアプローチ
GHCは、Haskellの言語仕様を実装しているコンパイラ・処理系の一つです。
GHCでは、Haskellの数学的な特徴と、現代のCPUアーキテクチャを親和させることに挑戦しています。
具体的には例えば、GHCは、複数の内部階層間で数学的な整合性を保ちながら、コンパイルを行うように設計されています。
2つの下位の内部階層(Core, STG)自体が、それぞれ、数学的な整合性を保つ、小さな関数型言語となっています。
また、下層のSTG層は、型無しラムダ計算の動作モデルを実現する抽象マシン(Abstruct Machine)として定義されており、関数的アプローチと、CPU構造を1対1で直接的に結びつけています。
これにより、GHCでは、Haskellの言語仕様を、数学的な整合性を保ちながら、物理的なCPUと結びつけることに成功しています。
一方で、それを実現するために、速度上のオーバーヘッドを背負っているという代償もあります。
GHC開発では今も、これらの必然的に背負っているオーバーヘッドを解消するために、様々な挑戦が続けられています。
しかし数学を知らないと使えないわけでもない
このようにHaskellは、数学の力をなんとか借用して問題に挑もうとする関数プログラミングをサポートする言語系の一つと考えられます。しかしHaskellを使用する上では、必ずしも常に数学の深い理解を必要とするわけではありません。
これは、CPUの構造に密接に結びつくC言語などを使用する際に、「トランジスタの電子の移動度」の理解が必ずしも必要ではないのと同様です。
コンピュータの階層設計によって、下位の階層設計者が頑張ってくれれば、上位層を使用する人は、その恩恵を自然と受けられることがあるためです。
もちろん、それらの理解があれば、多くの局面において、よりよい設計を行えると思います。
関数プログラミングだけが特別な解ではない
一方で、コンピューティングにおける性能向上や大規模化や品質向上に対するアプローチは、数学的、関数的アプローチ以外にもあるでしょう。
例えば、Erlang/Elixirに見られるような、"Let It Crash"もひとつの解かもしれません。
他にも、Goは、C言語の流れを継ぎながら、現代的なCPUへの最適化をより強める方向で、性能向上と生産性に挑戦するものと考えられます。
Haskellだけが特別な解ではない
数学的なアプローチを指向するプログラミング言語は、Haskellだけではありません。
例えば、OCamlは、数学的なアプローチと速度を両立させる挑戦を行っているものの一つと考えられます。
他にも、Rustは、C言語の流れを継ぐCPUへの直接操作と、数学的なアプローチを両立させる挑戦を行っているものの一つと考えられます。 Clojureは、従来のLispの先鋭的な特徴を継ぎながら、並行処理や豊富な既存プラットフォームとの親和性に挑戦しているものの一つと考えられます。 他にも様々な新旧の言語が、色々な観点でのアプローチを試みているのでしょう。
それでもHaskellも良いのは
繰り返しになりますが、Haskellは、数学的なアプローチでコンピューティングの課題に挑むという特徴を持つ言語の1つです。
しかし、Haskellの特徴は、関数型や数学的なアプローチといった面に限られません。
また、人々がHaskellに触れる理由も色々とあるでしょう。
数学的アプローチの実験場、商業的な事情、職業的な事情、globalなコミュニティの雰囲気、いつまでも全貌の見えない知識の森、なんとなく、、、などいろいろとあるでしょう。
ただもっと単純な理由でも良いでしょう。 もっと、単純な理由、それは、単に、
楽しいから
では! Enjoy computing!