State
エフェクトは
Reader
と
Writer
を組み合わせたものと見なせる。これらの操作を備えている。
get
現在の状態を取得
put
新しい状態を設定
次の例では、いくつかの状態を同時に追跡するためにタグも使えることを示している。
import cats.data._
import org.atnos.eff._, all._, syntax.all._
type S1[A] = State[Int, A]
type S2[A] = State[String, A]
type S = Fx.fx2[S1, S2]
val swapVariables: Eff[S, String] = for {
v1 <- get[S, Int]
v2 <- get[S, String]
_ <- put[S, Int](v2.size)
_ <- put[S, String](v1.toString)
w1 <- get[S, Int]
w2 <- get[S, String]
} yield "initial: "+(v1, v2).toString+", final: "+(w1, w2).toString
swapVariables.evalState(10).evalState("hello").run
> initial: (10,hello), final: (5,10)
この例では Eff[R, A]
の
A
を取得するために
eval
メソッドを使っているが、run
で値と状態の両方を取得することもできるし、exec
で状態だけを取得することもできる.
複数の State エフェクトを区別するのではなく、「小さな」状態を実行する State エフェクトを「より大きな」状態を実行する State エフェクトに変換することもできる。
import org.atnos.eff._, all._, syntax.all._
import cats.data.State
type Count[A] = State[Int, A]
type Sum[A] = State[Int, A]
type Mean[A] = State[(Int, Int), A]
type S1 = Fx.fx1[Count]
type S2 = Fx.fx1[Sum]
type S = Fx.fx1[Mean]
def count(list: List[Int]): Eff[S1, String] = for {
_ <- put(list.size)
} yield s"there are ${list.size} values"
def sum(list: List[Int]): Eff[S2, String] = {
val s = if (list.isEmpty) 0 else list.sum
for {
_ <- put(s)
} yield s"the sum is $s"
}
def mean(list: List[Int]): Eff[S, String] = for {
m1 <- count(list).lensState((_:(Int, Int))._1, (s: (Int,Int), i: Int) => (i, s._2))
m2 <- sum(list).lensState((_:(Int, Int))._2, (s: (Int, Int), i: Int) => (s._1, i))
} yield m1+"\n"+m2
mean(List(1, 2, 3)).runState((0, 0)).run
> (there are 3 values
the sum is 6,(3,6))