北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

MonadStateのgetって?

StateモナドはMonadStateになってまして、getとかputとか持ってるわけです。で、getすると状態が取れてput sすると状態がセットできる。


myCounter = do n <- get
               put $ n + 1
               return $ n + 1

doCount3 = do myCounter
              myCounter
              myCounter

countResult = evalState doCount3 0


でも、このgetとかputで使っている"カウントアップされている変数"の正体は一体なんなんでしょ? 実体が見えなくて、まるで魔法のようです。そんなわけで、軽く考えてみる。

まず、do記法ですが、こいつの正体は束縛なわけです。ってことは関数合成ですね。で、試しに(\x -> (v, y))のようなStateモナドに、getを束縛してみた。

getと束縛の定義からこんな感じですかね?


任意のxについて、(\x -> (v,y)) x = (v,y),
(\_ -> (\s -> (s,s)) v = (\s -> (s,s)),
(\s -> (s,s)) y = (y,y) なので、
(\x -> (v,y)) >>= (\_ -> (\s -> (s,s))) = (\x -> (y,y))

valueとしてステータスyを返すモナドに変わりました。これで次に束縛する関数からステータスが参照できるって仕組みですか。

同様に、これにput (n+1)を合成してみた。


(\x -> (y,y)) >>= (\n -> (\_ -> ( (), n + 1) ) ) = (\x -> ( (), y + 1) )

最初のステータスyに対して、1増加した値を返す写像となってくれた。バリューは空っぽなので、これにreturn vでバインドして値を含むようにしてみる。


return v = (\x -> (v, x) )なので、
(\x -> ( (), y + 1 )) >>= (\_ -> (\x -> (v, x) ) ) = (\x -> (v, y + 1))

最終的には、バリューはそのままでステータスが1増加したモナドが完成。getとputが利用している領域ってのは、モナドの返り値部分だったみたいですね。これらを参照・変更を繰り返すことでgetとputの機能を実現できると。で、evalStateで初期値を与えた時点で、溜め込んだこれらの処理が一気に走ると。

・・・また前みたいに間違ってたらごめんなさいw。勉強中のメモってことでご容赦をm(_ _)m。