State

State エフェクトは ReaderWriter を組み合わせたものと見なせる。これらの操作を備えている。

次の例では、いくつかの状態を同時に追跡するためにタグも使えることを示している。

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))