モナドスタックとliftingの図的理解
スタックは上へ伸びていく
一段のモナド
m a
なら
一般的に言う「モナドスタック」は「モナド」の「スタック」なので、上図のように
a
は含めないことが多いと思う

知らんけど。
直感的に型として理解しやすいので一応書いている
List Bool
なら
型引数を2つ取る場合のイメージ
State [Int] Int
Either String Int
なら
二段のモナド
ReaderT String (State [Int]) Int
なら
lifingのイメージ
liftの必要性が謎になった

普通は
do記法の中では、スタックの一番上のモナドとして見られる
m a
のときは以下のような型になる
hsf = do
a <- ma
undefined
a
は a
型になる
二段のモナドでもこれは同じ
hsf :: ReaderT Int (State [Int]) Bool
f = do
r <- ask -- a :: Int
undefined
f
がモナド変換子を使っていようがいまいが、 Reader Int
モナドを使っている感じになる
つまり、上のような書き方をしたときに a
の部分にStateが絡んで来ない
ではどうやってStateの部分にアクセスするかというときに
liftingを使う
liftingのイメージ
最上段にいるわけではないモナドを上段に上げる感じ
上段にliftすることで、
do記法の中でアクセスできるようになる
下部のモナドのmethodを使えるようになる
hsf :: 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
hsff :: 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
を省略してもコンパイルエラーにならないな

あってもなくてもエラーにならない
なんで?
あれ、liftって使うことあるの?
IOならliftIO使うとしたら、liftっていつ必要になるの?
IO関係ないモナドスタックでliftが必要になるサンプルコードを見たい
liftIOは何をしているか
前提として、IOモナドは常にモナドスタックの最下段にいる
IO用のmethodを呼ぶためにはネストの数だけliftを続ける必要がある
例えば3段モナドの場合
もっと多段になったときも同様
lift.lift.lift ... lift . print
のようにしないといけない

liftIOによって1発で一番上までliftさせることができる
ネストの深さに依存しない
runHogeについて
上から順に適用していく
上から剥いていく感じ
つまり、もっとも上にあるものが一番初めにrunされる
スタックが、Reader>State>Writerの場合
refhssolve :: 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を適用している

runReaderTが StateT Int (Writer [Int])) Int
を返している
そしてそれにrunStateTを適用する
参考