Ich gehe davon aus, scalaz 7.0.x und die folgenden Importe (siehe Antwortverlauf für scalaz 6.x ):
import scalaz._
import Scalaz._
Der Zustandstyp ist definiert als State[S, A]
wo S
ist der Typ des Zustands und A
ist der Typ des Wertes, der dekoriert wird. Die grundlegende Syntax zum Erstellen eines Statuswerts verwendet die folgende State[S, A]
Funktion:
val s = State[Int, String](i => (i + 1, "str"))
So führen Sie die Statusberechnung für einen Anfangswert aus:
s.eval(1)
s.exec(1)
s(1)
Der Status kann durch Funktionsaufrufe gefädelt werden. Um dies zu tun Function[A, B]
, definieren Sie Function[A, State[S, B]]]
. Verwenden Sie die State
Funktion ...
import java.util.Random
def dice() = State[Random, Int](r => (r, r.nextInt(6) + 1))
Dann kann die for/yield
Syntax verwendet werden, um Funktionen zu erstellen:
def TwoDice() = for {
r1 <- dice()
r2 <- dice()
} yield (r1, r2)
TwoDice().eval(new Random(1L))
Hier ist ein weiteres Beispiel. Füllen Sie eine Liste mit TwoDice()
Statusberechnungen.
val list = List.fill(10)(TwoDice())
Verwenden Sie die Sequenz, um eine zu erhalten State[Random, List[(Int,Int)]]
. Wir können einen Typalias bereitstellen.
type StateRandom[x] = State[Random,x]
val list2 = list.sequence[StateRandom, (Int,Int)]
val tenDoubleThrows2 = list2.eval(new Random(1L))
Oder wir können verwenden sequenceU
, um die Typen abzuleiten:
val list3 = list.sequenceU
val tenDoubleThrows3 = list3.eval(new Random(1L))
Ein weiteres Beispiel State[Map[Int, Int], Int]
für die Berechnung der Häufigkeit von Summen in der obigen Liste. freqSum
berechnet die Summe der Würfe und zählt die Frequenzen.
def freqSum(dice: (Int, Int)) = State[Map[Int,Int], Int]{ freq =>
val s = dice._1 + dice._2
val tuple = s -> (freq.getOrElse(s, 0) + 1)
(freq + tuple, s)
}
Verwenden Sie nun die Traverse, um freqSum
sie aufzutragen tenDoubleThrows
. traverse
ist äquivalent zu map(freqSum).sequence
.
type StateFreq[x] = State[Map[Int,Int],x]
tenDoubleThrows2.copoint.traverse[StateFreq, Int](freqSum).exec(Map[Int,Int]())
Oder genauer gesagt, indem Sie traverseU
die Typen ableiten:
tenDoubleThrows2.copoint.traverseU(freqSum).exec(Map[Int,Int]())
Beachten Sie, dass tenDoubleThrows2 , da State[S, A]
es sich um einen StateT[Id, S, A]
Typalias für handelt , als typisiert wird Id
. Ich copoint
verwandle es wieder in einen List
Typ.
Kurz gesagt, es scheint, dass der Schlüssel zur Verwendung von state darin besteht, dass Funktionen eine Funktion zurückgeben, die den Status und den gewünschten tatsächlichen Ergebniswert ändert ... Haftungsausschluss: Ich habe noch nie state
im Produktionscode verwendet, nur um ein Gefühl dafür zu bekommen.
Zusätzliche Infos zu @ziggystar Kommentar
Ich habe es aufgegeben, es zu versuchen, stateT
kann jemand anderes zeigen, ob StateFreq
oder StateRandom
kann erweitert werden, um die kombinierte Berechnung durchzuführen. Stattdessen habe ich festgestellt, dass die Zusammensetzung der beiden Zustandstransformatoren folgendermaßen kombiniert werden kann:
def stateBicompose[S, T, A, B](
f: State[S, A],
g: (A) => State[T, B]) = State[(S,T), B]{ case (s, t) =>
val (newS, a) = f(s)
val (newT, b) = g(a) apply t
(newS, newT) -> b
}
Es basiert auf g
einer Ein-Parameter-Funktion, die das Ergebnis des ersten Zustandstransformators aufnimmt und einen Zustandstransformator zurückgibt. Dann würde folgendes funktionieren:
def diceAndFreqSum = stateBicompose(TwoDice, freqSum)
type St2[x] = State[(Random, Map[Int,Int]), x]
List.fill(10)(diceAndFreqSum).sequence[St2, Int].exec((new Random(1L), Map[Int,Int]()))
State
Monade nicht wirklich ein "Staatstransformator"? Und als zweite Frage: Gibt es eine schönere Möglichkeit, das Würfeln und das Summieren in einer einzigen Staatsmonade zu kombinieren? Wie würden Sie das angesichts der beiden Monaden machen?