要求開発アライアンスのセッション『Object-Functional Analysis and Design: 次世代モデリングパラダイムへの道標』で使用するスライドについて背景説明を行ってきました。
今回は背景説明第4弾で、「圏論デザインパターン」として用意した以下の図を説明します。
圏論デザインパターン
関数型言語の技術マップで説明したように、型クラスの導入によって代数的構造や圏論の理論をプログラミング言語で直接利用できるようになりました。
代数構造的デザインパターンは、基本中の基本概念であるので、モノイド以外のパターンもいずれ広く使われるようになることが予想されますが、今の所広く使われているのは圏論デザインパターンの方です。
代表的な圏論デザインパターンは以下のものです。
- 圏(category)
- 対象と射(対象間の構造を保つ対応関係)によって表現される代数的構造
- 射(arrow,morphism)
- 圏で対象間の対応関係を表現
- 関手(functor)
- 2つの圏間の構造保持するマッピング
- Applicative functor
- 関手の一種。関手とモナドの中間に位置する性質を持つ。
- モナド(monad)
- 関手の一種。計算を表現するプログラミング構造 。
以下では、Scalaプログラミングの観点で説明します。
圏と射
圏は対象と射(対象間の構造を保つ対応関係)によって表現される代数的構造です。
圏論の基本概念(圏と射)は檜山さんの『はじめての圏論 その第1歩:しりとりの圏』が分かりやすいと思います。
圏論的には、HaskellプログラムはHask圏という圏の中で動いていることになりますが、海の中で泳いでいる魚が海を見れないように、自分自身(つまりHaskellプログラム)は圏を意識することはありません。「Scala圏」という用語はまだ聞いたことがありませんが、Scalaでも事情は同じでScalaプログラムが一種の圏になっていると考えるとよいでしょう。
Scalaと圏論の関係については『 Scalaで圏論入門』が参考になります。
圏を意識する必要があるのは、別世界の圏を操作することになった時です。
関数型プログラミングの中では、今の所、圏の一種であるクライスリ圏(kleisli category)が多く使用されています。クライスリ圏はモナドの合成に使用することができます。
逆にいうと、モナドの合成を行わない場合には登場してこないので、使用頻度は低いです。
ただし、いずれ並行プログラミングでモナドの合成などを行うようになると、かなり重要なデザインパターンになるのではないかと考えています。
関手
関手(functor)は圏内の対象と射の関係を保存する2つの圏間でのマッピングです。
Scalaでは、Listなどmapメソッドを持っているオブジェクトが関手ということになります。(ただ単にmapメソッドを持っているだけではなくて、いくつかの規則を守る必要があります。)
このブログでは以下の記事が参考になるかもしれません。
Applicative functor
Applicative functorは、関手の一種で、関手の上に持ち上げた関数を適用する機能を提供します。(普通の関手は、関手の上に持ち上げていない関数を内部的に持ち上げて適用します。)引数が2つ以上の関数を、(関手的な)コンテナに適用することができるのが特徴です。(この機能が便利だと思えるには、一定の関数型プログラミングの経験が必要なので、この説明で意味が分からなくても大丈夫です。)
Applicative functorは、Scalaの型クラスライブラリScalazで使用できます。
ScalaにおけるApplicative functorは『Applicatives are generalized functors』が参考になります。
このブログでは以下の記事が参考になるかもしれません。
モナド
モナド(monad)は、計算を表現するプログラミング構造で、モナド内に格納されたオブジェクトに対する計算文脈を提供します。そういう意味で、モナドはコンテナという意味合いと、計算文脈という意味合いの両方を持つ関数オブジェクトとして考えることができます。
Scalaでは、ざっくり言うと、ListなどflatMapメソッドを持っているオブジェクトがモナドということになります。(ただ単にflatMapメソッドを持っているだけではなくて、いくつかの規則を守る必要があります。)
Scalaではモナドは非常に重要な構成要素になっており、for式はモナドを扱う文法糖衣になっています。(一見Javaなどのfor文と同じものに見えますが、中ではモナドが動いている、という構造になっています。)
このブログでは以下の記事が参考になるかもしれません。
ノート
圏論の定義(圏、射、関手の基本定義ぐらいまで)そのものは、それほど難しいものではありませんが、圏論の教科書は初学者向けを謳っているものでも非常に難解です。
これは、将棋や囲碁で、ルールそのものは簡単であるにもかかわらず、実際に試合に勝つためには、序盤の定跡から、手筋、定石、大局観、詰将棋(詰碁)といった膨大な知識と運用能力を持っていなければならないことに似ています。
そういう意味で、ボクも圏論の触りのところは少し分かってきたとは思うのですが、圏論の全体像や具体的な活用方法については全く霧の中です。将棋に例えると圏論10級ぐらいですね。初段(数学科学部卒?)ぐらいになると客観的に見て形になってくるのではないかと思います。初段はハードルが高そうですが、関数型プログラマとしては、5級とか3級といった級の上位になってくると、十分に面白さが分かってきて、実用的に使えるようになってくるのではないかと推測しています。
ただ、圏論由来の関数型プログラミングのデザインパターンとしての関手、Applicative Functor、モナドについては、それなりにうまく使えるようになってきました。
ボクがテーマとして追いかけているモデル駆動のメタモデルやモデルコンパイラのような技術は圏論の知識が役に立ちそうなので、圏論の勉強は続けていきたいと思っていますが、一般的なアプリケーションプログラミングという意味では、関手、Applicative Functor、モナドの使い方をデザインパターンやイディオムという形で知っていれば十分で、圏論そのものの知識はあまり関係ないという印象です。
モナド
ボクが圏論を調べる時に使っている教科書の一つ『圏論による論理学 高階論理とトポス』では、モナドは出てきません。(少なくても索引には載っていません)
その他いろいろな状況証拠から、モナドは圏論の中では特殊な応用の一つ、ではないかという印象を持っています。
もちろん、圏論を修めた後、その一理論であるモナドを理解し、その全体像の中でのモナドを意識しつつ、プログラミングに応用するのは、理想的ではあるのですが、関数型プログラミングを習得するという目的にはかなり遠回りではないかと思われます。
Applicative Functor
この所、関数型プログラミングでは関手とモナドの中間の性質を持つApplicative Functorが注目されていますが、これは関数型プログラミングという「圏」で有効なメカニズムで、恐らく圏論そのものには(重要な理論の一つとしては)登場してこないのではないかと推測します。
このように関数型プログラミングの圏でのみ有用な技法というものはこれからもたくさん登場することが予想されます。こういった技法の習得は圏論を勉強しても直接は役に立たなさそうです。
Applicative Functorのようなアイデアを考案し理論として記述したり、そのようにして書かれた原著論文を解読するには圏論の知識が必要ですが、原著論文の内容をアプリケーションプログラムに必要なデザインパターンとして仕立て直した後は、関数型プログラミングの知識で技術を習得することは可能です。
以上のようなこともあり、関数型プログラミングの普及には、難しい理論を知らなくても使えるデザインパターンやイディオムが重要ではないかと考えています。
そういった観点から、このブログでは、デザインパターンやイディオムの整理を行っています。
圏論でのプログラミングというと,私が真っ先に連想するのはこのあたりです。
返信削除『CiNii 論文 - カテゴリー理論的関数型プログラミング言語』 http://ci.nii.ac.jp/naid/110003743564
『Categorical programming with inductive and coinductive types』 http://www.cs.ut.ee/~varmo/papers/thesis.pdf
いろんな応用がありますから,視野を広く保ちたいものですね。