
前回の記事では、関数型プログラミングを魅力的にしている特有の特徴について説明しました。本記事では、それらの概念が現代のプログラミングにどのように応用されているかを見ていきます。
関数型言語は、多くのプログラマーから「難解」「学術的」「理解しづらい」と見なされ、長年一定の距離を置かれてきました。しかし、コンピュータ分野全体は、その概念に強く引き寄せられてきました。コンピューティングの歴史を振り返ったり、あるいは手軽にAIチャットボットへ質問したりすれば、もともと関数型言語で生まれた概念が、現在では Python、Java、Rust などの「主流」言語にも数多く取り入れられていることが分かります。
私が特に気に入っている関数型プログラミングの概念は、高階関数(Higher-Order Functions)です。これは、変数の集合や配列に対して関数を適用する仕組みです。この技法によって、ソースコード量を大幅に削減でき、比較的読みやすいコードを書けるだけでなく、非常に洗練されたプログラミング表現も可能になります。『Programming Scala』の著者である Dean Wampler は、高階関数が「ビッグデータ時代」に登場した MapReduce の考え方とも結び付いていると指摘しています。
型クラス(Type Classes)は、多様なオブジェクトに共通の操作(ポリモーフィズム)を適用するうえで重要です。たとえば、図形に対する「サイズ変更」操作や、同じ型のオブジェクト同士をプラス記号で結合するような処理がこれにあたります。
次に挙げたいのがパターンマッチングです。これは複雑に入れ子になった if 文や、繰り返される else 文を置き換える優れた仕組みです。構文的には、パターンマッチングは C 言語の switch/case 文を拡張したようなものです。C の case ブロックでは単一の定数しか扱えませんが、パターンマッチングでは範囲指定、値のグループ化、null のような特別な値まで指定できます。さらに、異なるデータ型のブロックを扱うことも可能です。
クロージャ(Closure)も関数型プログラミングでは極めて重要であり、命令型プログラミングにおいても価値があります。私はこれを、C++ で許可されていたものの後に好ましくない設計と見なされるようになった「多重継承」の利点の一部を提供するものだと考えています。
さらに興味深い仕組みとして、レキシカルスコープ(Lexical Scoping)があります。これは変数の利用範囲を特定のコードブロック内に限定するものです。これによって、本来意味を持たない場面で変数が使われているようなエラーを発見しやすくなります。
ここで各言語の革新をすべて列挙するつもりはありません。中には重要な関数型の概念でありながら、より目立つプログラミング技法を支える裏方として存在するものもあります。また、私個人としては、複雑すぎて見合う価値がないと感じるものもあり、もっと扱いやすい構文に置き換えられるべきだと思うものもあります。
『Erlang Programming』および『Designing for Scalability with Erlang/OTP』の著者である Francesco Cesarini は、関数型言語がマイクロサービスの発展を先取りしていたと指摘しています。マイクロサービスは、命令型プログラミングにおいてモジュール性を実現するための人気手法です。関数型言語は、単一プロセッサ上でも、マルチコアシステム上でも、さらにはネットワーク越しでも効率よく動作するよう、関数を徹底的に分離します。
複数コアを効率的に活用し、データをメモリ上に保持することで、関数型プログラムは単一システム上でも非常に高いスケーラビリティを発揮し、比較的重いネットワーク通信を回避できます。また、現代のインターネット向け負荷に対応する形でスケールアウトすることも可能です。
しかし、関数型プログラミングから広まった巧妙な構文の先には、命令型プログラミングとは大きく異なるパラダイムが存在します。これこそが、関数型言語(定義は難しいにせよ)を依然として特別な存在にしており、多くのプログラマーが敬遠する理由でもあります。
命令型言語では、ループは繰り返し処理を扱う直感的な方法です。これは工場の自動化や、非営利団体が今でも大量郵便の封入作業に使うライン作業と同じように理解できます。関数型言語では、ループの代わりに再帰(特に末尾再帰)を使いますが、これは比較的わかりやすく、教えることも学ぶことも容易です。
私が関数型プログラミングを難しいと感じるのは、「副作用を完全に禁止する」という根本的かつ厳格な原則です。その結果、関数同士は変数(ましてやグローバル変数)を共有して、プログラムや環境の変化を追跡することができません。
これに対処する方法のひとつとして、Francesco Cesarini は状態データをキャッシュメモリで扱う手法を説明してくれました。(ここで、前回の記事で触れた Simon Peyton Jones のトランザクショナルメモリも重要になります。)また、Haskell の IO Monad も状態管理を助けます。別の著者は、「Haskell では、できる限り外側の層に到達するまで純粋性を維持しようとする」と述べています。
このようなグローバル変数の代替手法に加えて、関数型言語では文や関数が互いに複雑に包み込むような構造を取ります。これは、比較的階層構造が明確な命令型言語に比べ、多くの学習者にとって理解が難しいものです。
関数型プログラミング言語の歩みを説明するために、少し歴史の話をしたいと思います。題材はカードゲームの「コントラクトブリッジ」です。
ブリッジは20世紀初頭に誕生しました。シンプルなゲームであるホイストに、革新的なプレイヤーたちが高度なビッド(宣言)システムを加えたのです。長い間、アメリカでは Charles H. Goren が考案したビッドシステムが広く使われ、彼の本は多くのプレイヤーの本棚に並んでいました。
ビッドには高度な駆け引きがあり、繊細な思考が求められましたが、Goren のシステム自体は直感的でした。高いカードが多く、スペードが長く揃っていれば「1スペード」と宣言します。すると、パートナーが強いカードと長いダイヤを持っていれば、「2ダイヤ」と宣言します(各ビッドは前のビッドより高くなければならないためです)。
もちろん、「弱い2ビッド」や「プリエンプティブビッド」のような特殊ルールもありました。これらは、相手にもっと大きな得点を与えないため、あえて小さな損失を取る戦術です。しかし、基本的にはシステムは理解しやすく、多くの人々が楽しい時間をブリッジテーブルで過ごしていました。
ところが、上級者、特に競技用のデュプリケートブリッジを行うプレイヤーたちは、「1クラブ」という最も弱い宣言があまり役に立たないことに気づきました。そこで考案されたのが「ストロングクラブ」システムです。この方式では、「1クラブ」はクラブのスートとは無関係に、「私は強い手札を持っている」という意味になります。これにより、パートナー同士で最適な最終ビッドを探る余地が広がりました。
「ストロングクラブ」システムは、大半の手札では大きな違いを生みませんでした。しかし、デュプリケートブリッジでは、非常に強い手札のときに他テーブルのプレイヤーより少し有利になる可能性がありました。そのため、この方式は急速に広まりました。
しかし、「1クラブ」が本来の意味を失い純粋な記号になると、他にも記号的なビッドを導入する必要が生じ、ビッドは直感的な意味との結びつきを失っていきました。さらに、ビッドが記号化されると、多数の異なる「ストロングクラブ」システムが乱立するようになりました。そして複雑さに拍車をかけたのが、異なるビッドシステムを使う対戦相手への対応です。
要するに、ブリッジはもはや気軽な娯楽ではなくなったのです。友人同士で楽しむゲームではなく、プロ向けの競技になってしまいました。本格的にプレイするには、複数の非直感的な「ストロングクラブ」システムを暗記しなければなりません。そうでなければ、テーブル上の宣言のたびに相手から「そのビッドはあなたたちのシステムでは何を意味するのですか?」と確認されることになります。
ここで、このゲーム史の話を持ち出した理由を説明します。私は、多くのプログラミング言語――特に関数型言語――が、コンピューティングに対して「ストロングクラブ」がブリッジに与えたのと同じことをしていると考えています。これらの言語は、非常に複雑で多層的な思考を要求します。その結果、専門プログラマーと、シンプルな言語を学んで自分用の小さく便利なプログラムを書くアマチュアとの間に、高い壁が築かれてしまいました。(Basic、そしてその後の Perl、Tcl、Ruby、Python を思い出してください。)
もちろん、専門的な訓練を必要とするのは関数型言語だけではありません。たとえば、Apple の Swift を使って iPhone やその他の小型 Apple デバイス向けアプリを作りたいなら、Swift の不可解なメモリ管理について理解しなければなりません(これは、iPhone アプリ開発で最初に使われた Objective-C から受け継がれたものです)。また、コンピュータ科学者たちの現在のお気に入りともいえる Rust を学ぼうとしても、多くの難所が待ち構えています。
もっとも、本番品質のプログラミングが専門家だけのものになるのは、結果的には良いことなのかもしれません。アマチュアのプログラムはバグが多く、保守が難しく、セキュリティ攻撃の標的にもなりやすいからです。本シリーズ第1回で引用したトランザクショナルメモリの記事は、次のように締めくくられています。「誰にもわからない。10年後には、世界全体がトランザクショナルになっているかもしれない。」
2000年代初頭、多くの人々は「誰もがプログラミングを必要とする」と考えていました。この動きは「コンピュータリテラシー」と呼ばれていました。しかし現在、プログラミングの必要性は大きく変化しています。ソフトウェアは、1)ライブラリやオンラインサービスへ移行し、2)AI 支援を利用し、3)ハードウェアに組み込まれるようになっています。21世紀後半のプログラマーは、より高給で、より高学歴で、そして人数はより少なくなるのかもしれません。
You are currently viewing a placeholder content from Vimeo. To access the actual content, click the button below. Please note that doing so will share data with third-party providers.
More InformationYou are currently viewing a placeholder content from YouTube. To access the actual content, click the button below. Please note that doing so will share data with third-party providers.
More InformationYou need to load content from reCAPTCHA to submit the form. Please note that doing so will share data with third-party providers.
More InformationYou need to load content from reCAPTCHA to submit the form. Please note that doing so will share data with third-party providers.
More Information