STモナドとStateモナドのAPIの比較
内部実装はガン無視して、型と使用感の類似性を見たい
todos
IORefも加えると面白そう
samples
code:State.hs
stt :: State Integer ()
stt = do
modify (+i)
main :: IO ()
main = print $ runState stt 0 -- ((),55)
code:ST.hs
st :: ST s Integer
st = do
n <- newSTRef 0
modifySTRef n (+i)
readSTRef n
main :: IO ()
main = print $ runST st -- 55
状態の初期値は
Stateは、runの外部から与えている
STは、内部で生成している
型
code:State.hs
newtype State s a = State { runState :: s -> (a, s) }
code:ST.hs
newtype ST s a = ST (State# s -> (# State# s, a #)) 順序など細かい違いはあるがやっていることは同じ
モナドの定義
code:hs
instance Monad (State s) where
return a = State $ \s -> (a,s)
(State x) >>= f = State $ \s ->
let (a,s') = x s
(State y) = f a
in y s'
code:hs
instance Monad (ST s) where
return a = ST $ \s -> (# s, a #) (ST x) >>= f = ST $ \s ->
(ST y) = f a
in y s'
こうして比較すると、「引数と返り値で連鎖することで状態を表現している」点は同じであることがわかる
型上の表現が異なるだけで、関数の実体としてやっていることは全く同じ
STモナドは、メモリ割り当てを状態として捉えるStateモナドといえる
この定義が、元の定義と全く等価になっているのか微妙に自信がないmrsekut.icon
これで正しいなら割と良いものを書けたと思う
run
code:State.hs
runState :: s -> (a, s)
code:ST.hs
runST :: (forall s. ST s a) -> a
modify
code:State.hs
modify:: (s -> s) -> State s ()
modify f = state (\s -> ((), f s))
code:ST.hs
modifySTRef :: STRef s a -> (a -> a) -> ST s ()
modifySTRef ref f = writeSTRef ref . f =<< readSTRef ref
get
code:State.hs
get :: State s s
get = state (\s -> (s, s))
code:ST.hs
readSTRef :: STRef s a -> ST s a
readSTRef (STRef var#) = ST $ \s1# -> readMutVar# var# s1#