モナドに関する記事は世の中に沢山ありますが、モナドトランスフォーマーに関する記事はあまりないように思います。その概念が意味する所はHaskell初心者はもちろんのこと、他の言語使用者も知っておいて損は無いと思います。
モナドトランスフォーマー
モナドトランスフォーマーとは、「モナド変換子」とも言いますが、モナドと組み合わせる事で、新しいモナドを生み出すことが出来るデータ構造の事です。
例えばモナドIO
1とトランスフォーマーStateT s
を組み合わせると、StateT s IO
というモナドになります。組み合わせた結果がモナドなので、これもさらに他のトランスフォーマーと組み合わせる事も出来ます。そこでReaderT r
を組み合わせてみます。すると、ReaderT r (StateT s IO)
というモナドになります。さらにEitherT e
を組み合わせると、EitherT e (ReaderT r (StateT s IO))
となりました。これもやはりモナドです。
これらのモナドトランスフォーマーを組み合わせる事で、既存のモナドに他の機能を簡単に加えることが出来ます。
例えば先のEitherT e (ReaderT r (StateT s IO))
モナドは、
- IOが実行出来る
- 読み書き可能な状態
s
を持つ - 読み込みのみの値
r
を持つ - エラー
e
をthrow/catch等出来る
といった機能を持つことになります。
モナドがHaskell界で広く普及していることと同様に、モナドトランスフォーマーも現実問題を解くための具体的な手段として広く普及しています。モナドトランスフォーマーをライブラリとして提供することも珍しくありません。その方がモナド単体でライブラリ化することに比べて、汎用性が劇的に高くなるためです。
モナドトランスフォーマーのメリット、デメリット
モナドトランスフォーマーはモナドと簡単に組み合わせる事が出来、簡単に機能を追加出来ます。このことから幾つかの使い方が浮かびます。
- 既存のトランスフォーマーを組み合わせて欲しいモナドを素早く実現する
- トランスフォーマー同士を組み合わせて、新しいトランスフォーマーを構築、意味とヘルパーを添えてライブラリ化する
- トランスフォーマー内部のモナドを差し替えて、テスト時に挙動を変える
しかしモナドトランスフォーマーを重ねる事は、実行速度上のデメリットが発生します。その原因は後で見ますが、一般的にモジュラリティはパフォーマンスとのトレードオフとなる事が多いことは経験上、理解している人も多いでしょう。
ではパフォーマンスロストが発生するならばモナドトランスフォーマーの価値は下がるでしょうか?もちろん幾分かは下がるでしょうが、それを考慮に入れても十分に価値を提供してくれます。
例えばプロトタイピングです。プロトタイピングのためには開発速度が重要です。望みの機能を素早く実現するためにモナドトランスフォーマーは活躍してくれます。
例えば参照実装です。一度正しく動く実装が作れたならば、パフォーマンスの高い実装を作る時にも参照実装は役に立ちます。テスティングの際は参照実装との結果を比較すると良いでしょう。
例えばライブラリ化です。モナドトランスフォーマーと言う新しい機能の「切り分け方」は、今までになかったライブラリの在り方を促してくれます。
モナドトランスフォーマーとモジュラリティ
モナドトランスフォーマーの最大のメリットはそのモジュラリティにあります。
モナドは沢山の手続き的概念を一つにまとめ、統一的な扱い方を提供し、同一モナド上のモジュラリティ、モナドアクション同士の合成を提供してくれました。それ自体驚くべきことです。失敗可能性計算、状態付き計算、非決定計算、エラー処理を含む手続き、並行処理、並列計算、継続、参照を扱う処理、トランザクション処理、それらを組み合わせて特殊化した細かく小さい種々の手続き...多くの手続きがモナドという一つの概念でまとめて扱われ、どのモナド上でも自然にアクションが組み合わせられるのです。
モナドトランスフォーマーはそれに加え、さらに異なる切り口によるモジュラリティを提供してくれます。
John Hughesの「問題を部分に分解する能力は、解を貼り合せる能力に直接に依存している」2という指摘は、新しいモジュラリティが新しいライブラリの在り方を生み出すことを示唆しています。モナドトランスフォーマーを理解したら、Hackage3の中を色々探索してみると良いでしょう。他の言語とは異なった生態系を築いていることに気づくと思います。
モナドトランスフォーマーと設計
モナドトランスフォーマーは設計や実装方針、テスティングに関しても大きな影響を与えます。
正確にはモジュラリティが、設計や実装方針、テスティングに影響を与えます。思えばオブジェクト指向が世界で広がろうとしていた時、その厄介さは新しいモジュラリティへの対応だったのかもしれません4。それは設計を根底からひっくり返し得るからです。今回モナドトランスフォーマーのコストに対応するだけの価値はあるでしょうか?モナドトランスフォーマーの価値はモナドの価値を根拠にしていますが、モナドの汎用性と抽象度の高さは素晴らしいものです。ただし、モナドトランスフォーマーとその後の展望は確認しておいても良いでしょう、Haskellは未だに進化の速度が落ちていません。
モジュラリティが設計に与えるインパクトは一度まとめておきたいですね。これからも新しいモジュラリティが発見されるかもしれませんし。まあそれは別の話です。
ともあれ、初学者がHaskell文法に慣れてないうちにモナドやモナドトランスフォーマーの実践手法を学ぶことはそれなりに困難が伴うでしょう。もしそこに挑むのならば、その根っこがモジュラリティであることを意識しながら少しずつ進んでください。つまり、解くべき問題とモナドトランスフォーマーの特性をよく考えながら進んでください。
多分続きます。
-
モナドのkindは全て
* -> *
です、念のため。モナドIO :: * -> *
と モナドアクションIO a :: *
の差に注意してください。 ↩ -
http://www.sampou.org/haskell/article/whyfp.html 僕はこの言葉が大好きです:) ↩
-
オブジェクトは一種のモジュールですが、モジュラリティが余り高くないように思われます... ↩