Stateモナド
状態を扱うためのMonad型クラス
IOモナドなどは真の実装が処理系の内部にあるので中身が見れない
しかし、Stateモナドは内部実装もすべて見れるのでその点で理解がしやすい
自分で再定義できる
IOモナドもWorld -> (a, World)の様なイメージなのでそれを一般化したものがStateモナドだと考えることができる
型の定義
code: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の定義を見れば分かる通り。
定義
code: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を使って、以下のようにも書ける
code:hs
st >>= f = State $ \s ->
let (s', a) = runState st s
in runState (f a) s'
関数
state :: (s -> (a, s)) -> State s a
内部関数からStateモナドを作る関数
code:hs
import Control.Monad.State
main = do
let st = state $ \s -> (1, s)
print $ runState st ()
runState: : State s a -> s -> (a, s)
Stateモナドから値を取り出す
戻り値は(値, 状態)
code: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 ()
状態に関数を適用する
code: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で更新する
code: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と同じだね
参考