Hatena::Grouphaskell

[ pred x | x <- "Ibtlfmm!ojllj" ] RSSフィード

2009-05-14

StateT IO 21:45

本物のプログラマはHaskellを使う - 第6回 局所的な「状態」を利用するためのStateモナド: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