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 Sist der Typ des Zustands und Aist 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 StateFunktion ...
import java.util.Random
def dice() = State[Random, Int](r => (r, r.nextInt(6) + 1))
Dann kann die for/yieldSyntax 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. freqSumberechnet 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 freqSumsie aufzutragen tenDoubleThrows. traverseist ä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 traverseUdie 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 copointverwandle es wieder in einen ListTyp.
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 stateim Produktionscode verwendet, nur um ein Gefühl dafür zu bekommen.
Zusätzliche Infos zu @ziggystar Kommentar
Ich habe es aufgegeben, es zu versuchen, stateTkann jemand anderes zeigen, ob StateFreqoder StateRandomkann 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 geiner 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]()))
StateMonade 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?