generated at
Stateモナドのなにがうれしいのか
すごいH本 p.334の要約

まず、Stateモナドを使わずに、状態を扱うpopとpushを定義してみる
以下の関数では「Stackに何が積まれているか」は一種の状態だと考えることができる
Stack( = [Int]) をある種の状態の例として使う
hs
type Stack = [Int] -- Stackから一つ取り除き、取り除いたものと残りのStackを返す pop :: Stack -> (Int, Stack) pop (x:xs) = (x, xs) -- 加えるものと、Stackを与えると、結果としての()と加えられたStackを返す push :: Int -> Stack -> ((), Stack) push a xs = ((), a:xs)



ここで以下のようなstackMainp関数を考える
hs
stackMainp :: Stack -> (Int, Stack) stackMainp stack = let ((), newStack1) = push 3 stack (a , newStack2) = pop newStack1 in pop newStack2 -- popして、stackMainpの返り値として、値とスタックを返す main = do print $ stackMainp [11,12,13,14,15] -- 結果: (11,[12,13,14,15])
「3」をスタックに積んで、その後スタックから2つの値をpopする
popやpushの返り値の () a は使わないけど、関数の仕様上、受け取っている。
こういった状態付きの計算に、手で状態を与え、いちいち名前を付けて受け取っている
冗長で、大変
純粋関数の世界でモナドを使わずに愚直にやるとこんな実装になるmrsekut


Stateモナドを使うとこんなに簡素に書ける↓
doの中では、暗黙のstackに対してのアクションを実行している
hs
import Control.Monad.State type Stack = [Int] pop :: State Stack Int -- Stackを状態として持ち、Intを返す pop = state $ \(x:xs) -> (x, xs) -- (x, xs)は(Int, Stack) push :: Int -> State Stack () push a = state $ \xs -> ((), a:xs) stackMainp :: State Stack Int stackMainp = do push 3 pop pop main :: State [Int] main = do print $ runState stackMainp [11, 12, 13, 14, 15]
これで純粋関数の世界でも状態プログラミングができるようになる
手続き型のプログラミングをしているときは常時こんなコーディングをしている