generated at
モナドスタックとliftingの図的理解
主にモナド定義側関数目線の話
モナドスタックliftingを図で理解する

モナドスタックのイメージ
スタックは上へ伸びていく



一段のモナド
m a なら
一般的に言う「モナドスタック」は「モナド」の「スタック」なので、上図のように a は含めないことが多いと思うmrsekut
知らんけど。
直感的に型として理解しやすいので一応書いている
List Bool なら
型引数を2つ取る場合のイメージ
State [Int] Int
Either String Int なら




二段のモナド
モナド変換子を利用して二段のモナドになったときのイメージ
ReaderT String (State [Int]) Int なら



lifingのイメージ
途中で謎になったので #WIP にしておくmrsekutmrsekutmrsekutmrsekut
liftの必要性が謎になったmrsekut
普通はdo記法の中では、スタックの一番上のモナドとして見られる
m a のときは以下のような型になる
hs
f = do a <- ma undefined
a a 型になる
二段のモナドでもこれは同じ
hs
f :: ReaderT Int (State [Int]) Bool f = do r <- ask -- a :: Int undefined
f がモナド変換子を使っていようがいまいが、 Reader Int モナドを使っている感じになる
つまり、上のような書き方をしたときに a の部分にStateが絡んで来ない
ではどうやってStateの部分にアクセスするかというときにliftingを使う
liftingのイメージ
最上段にいるわけではないモナドを上段に上げる感じ
上段にliftすることで、do記法の中でアクセスできるようになる
下部のモナドのmethodを使えるようになる
hs
f :: ReaderT Int (State [Int]) Bool f = do r <- ask -- `Reader Int なんか`のInt s <- lift get -- `State [Int] なんか`の[Int] undefined
getはState用のmethodだが、liftすることでReaderのdoでも使えるようになる
3段になったときのlift
lift . lift
hs
ff :: ReaderT Int (StateT [Int] (Writer String)) Bool ff = do r <- ask -- `Reader Int なんか`のInt s <- lift get -- `State [Int] なんか`の[Int] w <- lift.lift.tell $ "hoge" -- `Writer String なんか`のString undefined
このコードの場合、 lift を省略してもコンパイルエラーにならないなmrsekut
あってもなくてもエラーにならない
なんで?
あれ、liftって使うことあるの?
IOならliftIO使うとしたら、liftっていつ必要になるの?
IO関係ないモナドスタックでliftが必要になるサンプルコードを見たい


liftIOは何をしているか
前提として、IOモナドは常にモナドスタックの最下段にいる
IO用のmethodを呼ぶためにはネストの数だけliftを続ける必要がある
例えば3段モナドの場合
もっと多段になったときも同様
lift.lift.lift ... lift . print のようにしないといけないmrsekut
liftIOによって1発で一番上までliftさせることができる
ネストの深さに依存しない


runHogeについて
上から順に適用していく
上から剥いていく感じ
つまり、もっとも上にあるものが一番初めにrunされる
スタックが、Reader>State>Writerの場合 ref
hs
solve :: ReaderT Int (StateT Int (Writer [Int])) Int solve = undefined main = do let r = 100 s = 0 res = runWriter (runStateT (runReaderT solve r) s) print res
最も最初にrunReaderTを適用しているmrsekut
runReaderTが StateT Int (Writer [Int])) Int を返している
そしてそれにrunStateTを適用する


参考