モナドトランスフォーマーとその周辺

モナドに関する記事は世の中に沢山ありますが、モナドトランスフォーマーに関する記事はあまりないように思います。その概念が意味する所はHaskell初心者はもちろんのこと、他の言語使用者も知っておいて損は無いと思います。


# モナドトランスフォーマー

モナドトランスフォーマーとは、「モナド変換子」とも言いますが、モナドと組み合わせる事で、新しいモナドを生み出すことが出来るデータ構造の事です。


例えばモナド`IO`[^1]とトランスフォーマー`StateT s`を組み合わせると、`StateT s IO`というモナドになります。組み合わせた結果がモナドなので、これもさらに他のトランスフォーマーと組み合わせる事も出来ます。そこで`ReaderT r`を組み合わせてみます。すると、`ReaderT r (StateT s IO)`というモナドになります。さらに`EitherT e`を組み合わせると、`EitherT e (ReaderT r (StateT s IO))`となりました。これもやはりモナドです。

[^1]: モナドのkindは全て `* -> *` です、念のため。モナド`IO :: * -> *` と モナドアクション`IO a :: *`の差に注意してください。

これらのモナドトランスフォーマーを組み合わせる事で、既存のモナドに他の機能を簡単に加えることが出来ます。

例えば先の`EitherT e (ReaderT r (StateT s IO))`モナドは、

* IOが実行出来る
* 読み書き可能な状態`s`を持つ
* 読み込みのみの値`r`を持つ
* エラー`e`をthrow/catch等出来る

といった機能を持つことになります。
モナドがHaskell界で広く普及していることと同様に、モナドトランスフォーマーも現実問題を解くための具体的な手段として広く普及しています。モナドトランスフォーマーをライブラリとして提供することも珍しくありません。その方がモナド単体でライブラリ化することに比べて、汎用性が劇的に高くなるためです。

# モナドトランスフォーマーのメリット、デメリット

モナドトランスフォーマーはモナドと簡単に組み合わせる事が出来、簡単に機能を追加出来ます。このことから幾つかの使い方が浮かびます。

* 既存のトランスフォーマーを組み合わせて欲しいモナドを素早く実現する
* トランスフォーマー同士を組み合わせて、新しいトランスフォーマーを構築、意味とヘルパーを添えてライブラリ化する
* トランスフォーマー内部のモナドを差し替えて、テスト時に挙動を変える


しかしモナドトランスフォーマーを重ねる事は、実行速度上のデメリットが発生します。その原因は後で見ますが、一般的にモジュラリティはパフォーマンスとのトレードオフとなる事が多いことは経験上、理解している人も多いでしょう。
ではパフォーマンスロストが発生するならばモナドトランスフォーマーの価値は下がるでしょうか？もちろん幾分かは下がるでしょうが、それを考慮に入れても十分に価値を提供してくれます。

例えばプロトタイピングです。プロトタイピングのためには開発速度が重要です。望みの機能を素早く実現するためにモナドトランスフォーマーは活躍してくれます。
例えば参照実装です。一度正しく動く実装が作れたならば、パフォーマンスの高い実装を作る時にも参照実装は役に立ちます。テスティングの際は参照実装との結果を比較すると良いでしょう。
例えばライブラリ化です。モナドトランスフォーマーと言う新しい機能の「切り分け方」は、今までになかったライブラリの在り方を促してくれます。

# モナドトランスフォーマーとモジュラリティ

モナドトランスフォーマーの最大のメリットはその<b>モジュラリティ</b>にあります。
モナドは沢山の手続き的概念を一つにまとめ、統一的な扱い方を提供し、同一モナド上のモジュラリティ、モナドアクション同士の合成を提供してくれました。それ自体驚くべきことです。失敗可能性計算、状態付き計算、非決定計算、エラー処理を含む手続き、並行処理、並列計算、継続、参照を扱う処理、トランザクション処理、それらを組み合わせて特殊化した細かく小さい種々の手続き...多くの手続きがモナドという一つの概念でまとめて扱われ、どのモナド上でも自然にアクションが組み合わせられるのです。
モナドトランスフォーマーはそれに加え、さらに異なる切り口によるモジュラリティを提供してくれます。

John Hughesの「問題を部分に分解する能力は、解を貼り合せる能力に直接に依存している」[^2]という指摘は、新しいモジュラリティが新しいライブラリの在り方を生み出すことを示唆しています。モナドトランスフォーマーを理解したら、Hackage[^3]の中を色々探索してみると良いでしょう。他の言語とは異なった生態系を築いていることに気づくと思います。

[^2]: http://www.sampou.org/haskell/article/whyfp.html 僕はこの言葉が大好きです:)
[^3]: http://hackage.haskell.org/

# モナドトランスフォーマーと設計

モナドトランスフォーマーは設計や実装方針、テスティングに関しても大きな影響を与えます。
正確には<b>モジュラリティが</b>、設計や実装方針、テスティングに影響を与えます。思えばオブジェクト指向が世界で広がろうとしていた時、その厄介さは新しいモジュラリティへの対応だったのかもしれません[^4]。それは設計を根底からひっくり返し得るからです。今回モナドトランスフォーマーのコストに対応するだけの価値はあるでしょうか？モナドトランスフォーマーの価値はモナドの価値を根拠にしていますが、モナドの汎用性と抽象度の高さは素晴らしいものです。ただし、モナドトランスフォーマーとその後の展望は確認しておいても良いでしょう、Haskellは未だに進化の速度が落ちていません。
モジュラリティが設計に与えるインパクトは一度まとめておきたいですね。これからも新しいモジュラリティが発見されるかもしれませんし。まあそれは別の話です。

ともあれ、初学者がHaskell文法に慣れてないうちにモナドやモナドトランスフォーマーの実践手法を学ぶことはそれなりに困難が伴うでしょう。もしそこに挑むのならば、その根っこがモジュラリティであることを意識しながら少しずつ進んでください。つまり、解くべき問題とモナドトランスフォーマーの特性をよく考えながら進んでください。

[^4]: オブジェクトは一種のモジュールですが、モジュラリティが余り高くないように思われます...


多分続きます。





<!--



「持ち上げ」という単語の出所は正確には把握していないのですが、恐らくmonadのreturnやFunctorのfmapあたりから来ているものだと思っています。純粋な値や関数を文脈上へ持ち上げる、という言い回しです。
そこから（イメージ上の）自然な拡張としてモナドトランスフォーマーのlift、「持ち上げ」という単語が発生し、さらには他の同種のアナロジーが使える所にも「持ち上げ」という単語が使われているように感じます。Haskeller達はこの「持ち上げ」という単語をやや濫用しつつ使っているように感じます。
もちろん、純粋関数やIOアクションを一つ関数噛ませるだけで色んな文脈上で使えるようになるという利点は非常に好ましいので、余り気にしている人はいないかもしれません。それでも初学者にとっては混乱の元でしょう。


そして「モナドスタック」についてですが、こちらは恐らくモナドトランスフォーマーの挙動から来ているものだと思われます。


https://hackage.haskell.org/package/transformers-0.4.2.0/docs/src/Control-Monad-Trans-State-Lazy.html#State

```hs
instance (Monad m) => Monad (StateT s m) where
    return a = state $ \ s -> (a, s)
    m >>= k  = StateT $ \ s -> do
        ~(a, s') <- runStateT m s
        runStateT (k a) s'
```

これは「`m`がモナドならば、`StateT s m`もモナドである」というインスタンス宣言です。
`m >>= k` の定義を見ると、

「もし`m >>= k`が（状態`s`を渡されつつ）runされるならば、
アクション`m`を実行した後、
その結果`a`を`k`に渡して実行する」

と読めます。
`StateT`を剥がして内部を実行した後、また`StateT`で包むということを繰り返しています。
この様子をスタックに見立てることで、
「`StateT`をpopして、その状態で何か処理をした後にまた`StateT`をpushする」
といった風にも考えられます。
これはモナドスタックが何重になっていても同様で、`(m >>= k)`とは、一度`m`のスタックを全てpopして(全てのモナドを上から順に走らせて)から全て下からpushし直し、その結果を使って`k`のスタックを全てpopして全てpushし直す、という非常に手間のかかる事をしている訳です。

モナドトランスフォーマーのMonadicな機能を組み合わせるというモジュラリティの高さはとても素晴らしいものですが、一方でパフォーマンスが犠牲になっているという事は指摘しておいても良いかもしれません。一般に、`(>>=)`は非常に呼ばれる回数が多いですから。モジュラリティがパフォーマンスとのトレードオフになりがちなことの一例ですね。
このパフォーマンス問題は、例えばモナドトランスフォーマーを全て潰したモナドを自作することで解決します。また、最近のExtensible Effects(Eff)はこれらモジュラリティを維持しつつパフォーマンス面も解決しようという試みの一つです。

http://konn-san.com/prog/haskell/extensible-effects.html


ともあれ、IOやIdentityがスタックの底であり、モナドスタックがFILOであることは確認できると思います。


# モナドスタックの積み方を変える

モナドスタックでは変換子をpopしたあとに同じ変換子をpushするのが基本的な使い方だと思いますが、popしたものと異なる変換子を積む事も可能です。それによってモナドで表現されたDSLを、他のモナド上で実行するといった事も可能になります。

http://d.hatena.ne.jp/ruicc/20141028/1414511568

```hs
runDSL :: DSL a -> MyMonad a
```

-->