2009-05-14
WinGhci
ソフト |
ghciのGUIフロントエンドを見つけた。コマンドプロンプトで直接GHCiを使うのと比べて特段プログラムに便利なわけではないが、ツールを登録したりフォントが変えられたりするのは地味にうれしい。それとセッションをまたがって入力の履歴が残るのと、ログをコピーしても矩形選択にならない。同梱のinstall.exeを実行するとwinghciが.hsファイルに関連付けされる。
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で実践してみたが、まだメモが雑然としている。そのうち整理したい。
問題:StateTはselfが暗黙
ひとつ問題がある。これでは一度に一つのオブジェクトしか操作できない。
get、put、modifyをもとに自身の状態を更新する関数を作ることはできるのだが、他の状態を変更しようとするとエラーになる。
Prelude Control.Monad.State> flip runStateT "hoge" $ flip runStateT 1 $ put "foo" <interactive>:1:39: No instance for (Num [Char]) arising from the literal `1' at <interactive>:1:39 Possible fix: add an instance declaration for (Num [Char]) In the second argument of `flip', namely `1' In the first argument of `($)', namely `flip runStateT 1' In the second argument of `($)', namely `flip runStateT 1 $ put "moga"'
もっとも内側のrunStateTで使う型が優先されるようだ。
liftとliftIOの違い
liftは変換子を使った数だけ重ねる必要があるが、liftIOでは何度合成しても一回ですむ。代わりにliftIOはIOの値にしか使えない。
HaskellWikiには、これをIO以外にも使えるようにしたMonadBaseというものがある。
Prelude> :m +Control.Monad.State Control.Monad.Identity Prelude Control.Monad.State Control.Monad.Cont> flip runContT return $ flip runStateT 1 $ do put 3; x <- get; liftIO $ print x; modify (* 10); y <- get; liftIO $ print y 3 30 ((),30) Prelude Control.Monad.State Control.Monad.Cont> flip runContT return $ flip runStateT 1 $ do put 3; x <- get; lift $ print x; modify (* 10); y <- get; lift $ print y <interactive>:1:23: Couldn't match expected type `ContT r m r' against inferred type `IO ((), b)' In the second argument of `($)', namely `flip runStateT 1 $ do put 3 x <- get lift $ print x modify (* 10) ....' In the expression: flip runContT return $ flip runStateT 1 $ do put 3 x <- get lift $ print x modify (* 10) .... In the definition of `it': it = flip runContT return $ flip runStateT 1 $ do put 3 x <- get lift $ print x .... Prelude Control.Monad.State Control.Monad.Cont> flip runContT return $ flip runStateT 1 $ do put 3; x <- get; lift $ lift $ print x; modify (* 10); y <- get; lift $ lift $ print y 3 30 ((),30)

