Hatena::Grouphaskell

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

2009-05-14

[]WinGhci 20:56

f:id:illillli:20090514221134p:image:right:w180

ghciGUIフロントエンドを見つけた。コマンドプロンプトで直接GHCiを使うのと比べて特段プログラムに便利なわけではないが、ツールを登録したりフォントが変えられたりするのは地味にうれしい。それとセッションをまたがって入力の履歴が残るのと、ログをコピーしても矩形選択にならない。同梱のinstall.exeを実行するとwinghciが.hsファイルに関連付けされる。

[]jEdit 22:17

f:id:illillli:20090514221133p:image:right:w180

テキストエディタ。UIが英語のみなのが玉に瑕だが、Haskellを標準である程度ハイライトしてくれる。BufferTabsプラグインが便利。

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で実践してみたが、まだメモが雑然としている。そのうち整理したい。

問題:StateTはselfが暗黙 21:59

ひとつ問題がある。これでは一度に一つのオブジェクトしか操作できない。

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の違い 21:45

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)
トラックバック - http://haskell.g.hatena.ne.jp/illillli/20090514