先に作った電卓のレジスタのように Haskell でも状態を保持しておきたいときがあります。
先の電卓ではIORefを使ってレジスタの状態を保持しました。
- IORef
> :m + Data.IORef > do{ x <- newIORef 1; modifyIORef x (+2); ret <-readIORef x; return ret} -- => 3 > :t do{ x <- newIORef 1;modifyIORef x (+2);ret <-readIORef x;return ret} do{ x <- newIORef 1;modifyIORef x (+2);ret <-readIORef x;return ret} :: Num b => IO b
IO 型ですから、この先ずっとIO 型がつきまとうことになります。
- Stateモナド(Control.Monad.State)
- Haskell: Control.Monad.Stateメモ Stateモナドはここが詳しい。
> :m + Control.Monad.State > execState (do{n <- get; put (n+20)}) 10 -- => 30 > execState (do{n <- get; put (n+20); n <- get;put (n+30)}) 10 -- => 60 > 40 + execState (do{n <- get; put (n+20); n <- get; put (n+30)}) 10 -- => 100 > runState (do{n <- get; put (n+20);n2<-get;return [n,n2]}) 10 -- => ([10,30],30) > evalState (do{n <- get; put (n+20);n2<-get;return [n,n2]}) 10 -- => [10,30] > execState (do{n <- get; put (n+20);n2<-get;return [n,n2]}) 10 -- => 30
execState の中なら変数のように使えます。execStateの外に返す値には余分な型がついていませんので、そのまま演算ができます。
> runState (do{ xs <- get; put (123:xs);xs <- get; put (100:xs)}) [10..15] -- > ((),[100,123,10,11,12,13,14,15])
> :m + Control.Monad.ST > :m + Data.STRef > runST (do{ x <- newSTRef 100; modifySTRef x (+200); ret <- readSTRef x; return ret}) -- => 300 > 300 + runST (do{ x <- newSTRef 100; modifySTRef x (+200); ret <- readSTRef x; return ret}) -- => 600
STモナドでも、runST の中なら変数のように使えます。runST の外でも、そのまま演算が続行できます。