Haskell で変数を使う。(IORef、Stateモナド、STモナド)

先に作った電卓レジスタのように 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 型がつきまとうことになります。

> :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 の外でも、そのまま演算が続行できます。