2009-05-14
StateT IO
第6回 局所的な「状態」を利用するためのStateモナド - 本物のプログラマはHaskellを使う:ITproの記事を見ながら手でいじった記録。
言うまでもないが、Haskellはオブジェクト指向ではない。純粋で関数型で遅延的だ。ということで、データを変換する関数と無限リストだけで禁欲的にプログラムを書いて、最後にmain以下のIOの嵐で鬱憤を晴らすのが最初に試す方法だろう。プログラマの趣味にもよるが、少なくとも僕はこれだと無理が出てくる。
「Stateモナドを使えば状態が扱える」という噂があるが、しばらくいじってみても結局関数の塊だとわかる(つまり、i++はできてもprintができない)。あと何だかrunStateの引数の順番が気に食わない。以下ではflipで対処したが、コードではwithStateと名前をつけたい。
Prelude Control.Monad.State> :m Control.Monad.State Prelude Control.Monad.State> :i State newtype State s a = State {runState :: s -> (a, s)} : instance MonadState s (State s) Prelude Control.Monad.State> :i MonadState class (Monad m) => MonadState s m | m -> s where get :: m s put :: s -> m () : Prelude Control.Monad.State> :t runState get runState get :: s -> (s, s) Prelude Control.Monad.State> runState get 1 (1,1) Prelude Control.Monad.State> flip runState 1 get (1,1) Prelude Control.Monad.State> flip runState 1 $ do get (1,1) Prelude Control.Monad.State> flip runState 1 $ do put 3; get (3,3) Prelude Control.Monad.State> flip runState 1 $ do put 3; x <- get; print x <interactive>:1:38: Couldn't match expected type `State b a' against inferred type `IO ()' In the expression: print x In the second argument of `($)', namely `do put 3 x <- get print x' In the expression: flip runState 1 $ do put 3 x <- get print x Prelude Control.Monad.State> flip runState 1 $ do put 3; x <- get; modify (* 10); y <- get; return (x,y) ((3,30),30)
モナドのすべてでは、ここでStateの代わりにStateTというモナド変換子を使うとStateを使いながらIOができると謳う。
Prelude Control.Monad.State> :i StateT newtype StateT s m a = StateT {runStateT :: s -> m (a, s)} : Prelude Control.Monad.State> flip runStateT 1 $ do put 3; x <- get; print x <interactive>:1:39: Couldn't match expected type `StateT b m a' against inferred type `IO ()' In the expression: print x In the second argument of `($)', namely `do put 3 x <- get print x' In the expression: flip runStateT 1 $ do put 3 x <- get print x
IO ()をStateTの値にするにはliftかliftIOを使うそうだ。
Prelude Control.Monad.State> flip runStateT 1 $ do put 3; x <- get; liftIO $ print x 3 ((),3) Prelude Control.Monad.State> flip runStateT 1 $ do put 3; x <- get; liftIO $ print x; modify (* 10); y <- get; liftIO $ print y 3 30 ((),30)
これでi++をしながらprintができる。
つまり、オブジェクト思考的なことにはStateT a IO bという型がつく。aは状態のデータ型で、上ではIntになっている。
id:illillli:20080228で実践してみたが、まだメモが雑然としている。そのうち整理したい。
コメントを書く
トラックバック - http://haskell.g.hatena.ne.jp/illillli/20090514