2011年12月24日土曜日

State, StateT

クリスマスイブらしいよ!
私は家で引きこもってるけど!
State

状態を保持する計算に用いられます。

val s = for {
i <- init[Int]
_ <- modify[Int](_ |+| 5)
j <- gets[Int, Int](_ |+| 5)
} yield i |+| j
s ! 0 assert_=== 10
s ! 5 assert_=== 20
view raw state1.scala hosted with ❤ by GitHub

Stateは!で値を、~>で状態を、applyで両方をとりだせます。
値、状態を取り出すときには、初期状態を与えなければなりません。

val s = (put(5) >|> state[Int, Int](s => (s * 2, s + 10)))
s ! mzero[Int] assert_=== 15
s ~> mzero[Int] assert_=== 10
s(mzero[Int]) assert_=== (10, 15)
view raw state2.scala hosted with ❤ by GitHub


値や状態を操作するための関数はStatesに定義されています。

init

状態で値を初期化します。

put

状態を上書きします。

gets

状態から値を更新します。

modify

状態から状態を更新します。

state

状態から値と状態を更新します。


IdentityにもStateを作る関数が定義されています。

state

呼び出し元を初期値にします。

constantState

呼び出し元を初期値にし、初期状態を与えます。

val s = for {
i <- 10.state[Int]
j <- gets[Int, Int](_ |+| 5)
k <- 10.constantState(5)
} yield i |+| j |+| k
s ! 5 assert_=== 30
s ! 10 assert_=== 35
s ~> 0 assert_=== 5
view raw state3.scala hosted with ❤ by GitHub

State#withsは入力される状態を更新します。

init[Int].withs(_ |+| 5) ~> 5 assert_=== 10
5.state[Int].withs(_.times(2)) ~> 5 assert_=== 10
put(5).withs(_ |+| 100) ~> 10 assert_=== 5
view raw state4.scala hosted with ❤ by GitHub

StateT


モナド変換子版もあります。

val s = for {
i <- stateT[Option, Int, Int](_.some <|*|> 100.some)
j <- stateT[Option, Int, Int](s => (s + 100).some <|*|> s.some)
} yield i |+| j
s ! 100 assert_=== Some(200)
s ~> 100 assert_=== Some(200)
view raw statet1.scala hosted with ❤ by GitHub

mapsにより別のモナドにmapすることもできます。

val s = for {
i <- stateT[Option, Int, String](_.some <|*|> "Scala".some).maps {
case Some((s, a)) => List((s |+| 2, a |+| "z"), (s.times(2), a |+| "ちゃん"))
case None => nil
}
j <- stateT[List, Int, String](s => (s |+| 100, s.shows |+| "倍かわいい").pure[List])
} yield i |+| j
s ! 10 assert_=== List("Scalaz12倍かわいい", "Scalaちゃん20倍かわいい")
view raw statet2.scala hosted with ❤ by GitHub

Writerは結合していくことしか出来ませんでしたが、Stateではもう少し自由度が高いというイメージです。
ScalaはvarがあるからStateモナドとかいらないのでは?と思っていましたが、実際に調べて書いてみると可変な参照とはまた違ったもので、これはこれで使い方がありそうです。
STモナドはいりません。

0 件のコメント:

コメントを投稿