generated at
Stateモナド
状態を扱うためのMonad型クラス
IOモナドSTモナドのオープンな一般化のようなもの
IOモナドなどは真の実装が処理系の内部にあるので中身が見れない
しかし、Stateモナドは内部実装もすべて見れるのでその点で理解がしやすい
自分で再定義できる
IOモナドも World -> (a, World) の様なイメージなのでそれを一般化したものがStateモナドだと考えることができる



型の定義
hs
newtype State s a = State { runState :: s -> (a, s) }
s : 状態を表す型。型変数のままでも、具体的な型でも良い
a : Stateモナドの中に含まれる値の型
「状態」の世界から、Stateモナドから取り出したときの型
モナド定義側関数では気にすることはない
s -> (a, s) は、 前の状態 -> (値, 次の状態)
Maybeとかを考えているときは、
「モナド値」と言えば単に Maybe Int を想像しとけば良かったが、
Stateモナドではモナド値は State (s -> (a, s)) になると言っている
↓の return の定義を見れば分かる通り。


定義
hs
instance Monad (State s) where return a = State $ \s -> (a,s) (State x) >>= f = State $ \s -> -- ① let (a,s') = x s -- ② in runState (f a) s' -- ③
return の方を見てみる
\前の状態 -> (a,前の状態) という風に、特に状態を操作せずにそのまま返しているだけ
bind の方を見てみる
具体的な型は、 (>>=) :: State s a -> (a -> State s b) -> State s b
①の \s -> が前の状態
②で、前の状態に対して、 (新しい状態,計算結果) を得る
③で、新しい状態に対して、runState
普通はそうしないが、2回 runState を使って、以下のようにも書ける
hs
st >>= f = State $ \s -> let (s', a) = runState st s in runState (f a) s'



関数
state :: (s -> (a, s)) -> State s a
内部関数からStateモナドを作る関数
hs
import Control.Monad.State main = do let st = state $ \s -> (1, s) print $ runState st ()
runState: : State s a -> s -> (a, s)
Stateモナドから値を取り出す
戻り値は (値, 状態)
hs
import Control.Monad.State main = do let a = return 1 :: State s Int print $ runState a () -- ()は初期状態(必須)
evalState:: State s a -> s -> a
値だけを取り出す
execState:: State s a -> s -> s
状態だけを取り出す



Stateアクション
get:: State s s
状態を読み取る
put:: s -> State s ()
状態を書き換える
modify:: (s -> s) -> State s ()
状態に関数を適用する
hs
import Control.Monad.State test = do -- 状態 -> (値, 状態) a <- get -- 状態を取得 put $ a + 1 -- 状態+1を新しい状態に設定 modify (* 2) -- 状態に関数を適用 return a -- 最初の状態を値として返す main = do print $ runState test 5 -- 結果: (5, 12)



Recordのstateをputで更新する
hs
import Control.Monad.State import Data.Functor.Identity data User = User { _id :: Int, _name :: String } deriving (Show) updateId :: StateT User Identity () updateId = do user <- get let id = _id user put $ user { _id = id + 1 } -- こうやって更新する main :: IO () main = print $ runIdentity $ runStateT updateId (User 1 "Kota")
Elmと同じだね





参考
Lensの話