モナドとモナド変換子のイメージを描いてみた

最初に言っておくと、モナドって何なの?っていう答えは一切ないです。
自分にとってモナドは「とりあえず型さえ合わせておけば何かいろいろしてくれる奴」程度としか認識できていないので、そんな説明できないです。
で、そんな自分が脳内でどういう風にイメージしてモナドモナド変換子の混ざったコードを書いているかというのを図に表してみました。


ここら辺の話を図にした感じです。

モナド

モナドには return 関数と >>= 関数があります。


こんなイメージです。下側の線が普通の関数型の世界、上側の線がモナドの世界です。
どちらもモナド側の出力しか無いので、どちらかの関数を使ったら、モナドから脱出することはできません。


ただ、>>= 関数の右側が点線の箱になっていることが分かるでしょうか。
ここには、太線が示すように、関数型の世界の値を受けてモナドの世界の値を返す関数(a -> m b な関数)なら何でも入れることができます。
a -> b な関数を適用したい場合も簡単です。 return と組み合わせるだけでいいのです。

こんな感じになります。

ちなみにこの m >>= return.f と同じことをしてくれる関数があります。
それは liftM です。

liftM の入出力と、f 関数の入出力だけに注目すると、入力と出力を持ち上げてる(lift)感じがします。

で、後はこれをどんどん繋いで、処理を連鎖させるだけです。


例えば IO モナド

まあこれは print 10 と同じなのですけれども、型さえ合わせればこんな風に書くことができます。
getLine という、標準入力から入力を受け付ける getLine 関数(getLine :: IO String)があるのですが、この入力された文字列を受け取って表示するなら、

こんな風になります。
こうすれば、>>= 関数の中でいろいろやってくれて、普通の世界では扱えない IO とかも扱ってくれるようになりますし、いろんなモナドをうまく繋いでいい感じに処理することができるようになります。

モナド変換子

モナド変換子を使うと、そのモナドの中で任意のモナドを扱うことができるようになります。
例えば MaybeT IO Int なら、Maybe でいろんな処理を繋いでいる途中に IO の処理を混ぜて連鎖させることができるようになります。


モナド変換子は、関数型の世界と、モナドの世界と、モナド変換子の世界の3つに分かれています。
モナド変換子もモナドの一種なので、return 演算子と >>= があります。


関数型の世界と、モナド変換子の世界が、↑で説明したモナドと全く同じになっていることが分かります。


そして更にモナド変換子には、lift という関数が付いてきます。

モナドの世界からモナド変換子の世界に変換してくれる関数です。
liftM と違って入力と出力を両方持ち上げるわけではなくて、どちらかというと return に近いですね。


これらを組み合わせて、モナド変換子の世界を作り上げます。

T の世界が何のモナド変換子かというのは特に明示していないのですが、とりあえず f 関数(普通の関数)と print 関数(IO の関数)が同じように >>= で連鎖していることが分かります。


この最終的に出力されたモナド変換子の値をどうすればいいかについては、それぞれのモナド変換子の実装によって変わります。
例えば MaybeT というモナド変換子の場合は runMaybeT というのが用意されていて、こいつにモナド変換子の値を渡すことになります。
それぞれドキュメントを読みましょう。