人気ブログランキング | 話題のタグを見る

State モナド

State モナドにも挑戦してみようと思った。しかし、State モナドの型は次のようになる。

Prelude> :m Control.Monad.State
Prelude Control.Monad.State> :info State
newtype State s a = State {runState :: s -> (a, s)}
-- Defined in Control.Monad.State.Lazy
instance Monad (State s) -- Defined in Control.Monad.State.Lazy
instance Functor (State s) -- Defined in Control.Monad.State.Lazy
instance MonadFix (State s) -- Defined in Control.Monad.State.Lazy
instance MonadState s (State s)
-- Defined in Control.Monad.State.Lazy

データコンストラクタ State のパラメータが {runState :: s -> (a,s)} だ。フィールド名 runState がついている上に、パラメータの型が s -> (a, s) だ。つまり引数が s で戻り値が (a, s) の「関数」だ。関数をパラメータにとるデータコンストラクタって何だ???

仕方がないから、引数 s で戻り値 (a, s) の関数 \s -> ("hello", s) を作って、コンストラクタ State のパラメータにしてみた。

Prelude Control.Monad.State> let s = State (\s -> ("hello", s))

s の型はどうなっているだろうか。

Prelude Control.Monad.State> :t s
s :: State s [Char]

State 型だが、このままではどういう風に使うのか検討もつかない。しかし、State のコンテナにフィールド runState がついていたので、runState s は引数1個の関数になるはずだ。そこで、試してみた。

Prelude Control.Monad.State> runState s "world"
Loading package syb-0.1.0.2 ... linking ... done.
Loading package base-3.0.3.2 ... linking ... done.
Loading package mtl-1.1.0.2 ... linking ... done.
("hello","world")

確かに関数として働かせることができた。\s -> ("hello", s) の s に "world" が代入されている。どうも、State 型のデータは、runState の引数にすると関数として働くようだ。こういうふうに自前で定義したState型のデータのほかに、State 型には、あらかじめ定義された、get という関数と put a という関数がある。それらの動作も確かめてみた。

Prelude Control.Monad.State> runState (get) "hello"
("hello","hello")
Prelude Control.Monad.State> runState (put "world") "hello"
((),"world")

get は一個の引数 s をとり、(s, s) を返し、put は二つの引数 a s をとり、((), a) を返す。注意しないといけないのは、get も put も runState でフィールド指定したときにのみ関数として働くことができる。

しかし、これのどこがモナドなのだろうか。そこで、思いついたのだが、get と put が >>= 結合できないかということだ。IO モナドの getLine と putStrLn からの類推だ。そこでやってみた。

Prelude Control.Monad.State> runState (get >>= put) "hello"
((),"hello")

確かに get が引数 "hello" を拾い上げ、put に渡して ((), "hello") と "hello" をタプルに入れて返している。

それでは、do 記法も使えるのだろうか。

Prelude Control.Monad.State> runState (do a <- get; put a) "hello"
((),"hello")

使えたようだ。それでは return も使えるだろうか。

Prelude Control.Monad.State> runState (return "hi" >>= put) "hello"
((),"hi")

使えた。どうやら、State モナドは runState の括弧の中で記述するようだ。

何がモナドなのかがなんとなく分かってきたので、最後に自前の State モナドを使ってget , put と連携させてみた。

Prelude Control.Monad.State> let s1 a = State (\s -> ("nice to meet you, " ++ a, s))
Prelude Control.Monad.State> runState (get >>= s1 >>= put) "world"
((),"nice to meet you, world")

何となく State モナドがモナドなんだという気がしてきた。結局 >>= で数珠繋ぎにできるのがモナドなのだろう。
by tnomura9 | 2011-11-05 18:50 | Haskell | Comments(0)
<< Stateモナドの中身 liftM >>