Asumo, scalaz 7.0.x y las siguientes importaciones (mirar la historia respuesta para scalaz 6.x):
import scalaz._
import Scalaz._
El tipo de estado se define como State[S, A]
donde S
es escribir del estado y A
es el tipo del valor que se está decorando. La sintaxis básica para crear un valor de estado hace uso de la función State[S, A]
:
// Create a state computation incrementing the state and returning the "str" value
val s = State[Int, String](i => (i + 1, "str"))
Para ejecutar el cálculo del estado en un valor inicial:
// start with state of 1, pass it to s
s.eval(1)
// returns result value "str"
// same but only retrieve the state
s.exec(1)
// 2
// get both state and value
s(1) // or s.run(1)
// (2, "str")
El estado se puede pasar por las llamadas a funciones. Para hacer esto en lugar de Function[A, B]
, defina Function[A, State[S, B]]]
. Utilice la función State
...
import java.util.Random
def dice() = State[Random, Int](r => (r, r.nextInt(6) + 1))
A continuación, la sintaxis for/yield
se puede utilizar para componer funciones:
def TwoDice() = for {
r1 <- dice()
r2 <- dice()
} yield (r1, r2)
// start with a known seed
TwoDice().eval(new Random(1L))
// resulting value is (Int, Int) = (4,5)
Aquí es otro ejemplo. Complete una lista con TwoDice()
cálculos de estado.
val list = List.fill(10)(TwoDice())
// List[scalaz.IndexedStateT[scalaz.Id.Id,Random,Random,(Int, Int)]]
Usa la secuencia para obtener un State[Random, List[(Int,Int)]]
. Podemos proporcionar un alias tipo.
type StateRandom[x] = State[Random,x]
val list2 = list.sequence[StateRandom, (Int,Int)]
// list2: StateRandom[List[(Int, Int)]] = ...
// run this computation starting with state new Random(1L)
val tenDoubleThrows2 = list2.eval(new Random(1L))
// tenDoubleThrows2 : scalaz.Id.Id[List[(Int, Int)]] =
// List((4,5), (2,4), (3,5), (3,5), (5,5), (2,2), (2,4), (1,5), (3,1), (1,6))
O podemos utilizar sequenceU
que inferir los tipos:
val list3 = list.sequenceU
val tenDoubleThrows3 = list3.eval(new Random(1L))
// tenDoubleThrows3 : scalaz.Id.Id[List[(Int, Int)]] =
// List((4,5), (2,4), (3,5), (3,5), (5,5), (2,2), (2,4), (1,5), (3,1), (1,6))
Otro ejemplo con State[Map[Int, Int], Int]
para calcular la frecuencia de las cantidades en la lista anterior. freqSum
calcula la suma de los lanzamientos y las frecuencias de conteos.
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)
}
Ahora usa transversal para aplicar freqSum
sobre tenDoubleThrows
. traverse
es equivalente a map(freqSum).sequence
.
type StateFreq[x] = State[Map[Int,Int],x]
// only get the state
tenDoubleThrows2.copoint.traverse[StateFreq, Int](freqSum).exec(Map[Int,Int]())
// Map(10 -> 1, 6 -> 3, 9 -> 1, 7 -> 1, 8 -> 2, 4 -> 2) : scalaz.Id.Id[Map[Int,Int]]
O más brevemente utilizando traverseU
para inferir los tipos:
tenDoubleThrows2.copoint.traverseU(freqSum).exec(Map[Int,Int]())
// Map(10 -> 1, 6 -> 3, 9 -> 1, 7 -> 1, 8 -> 2, 4 -> 2) : scalaz.Id.Id[Map[Int,Int]]
Tenga en cuenta que debido a State[S, A]
es un alias de tipo para StateT[Id, S, A]
, tenDoubleThrows2 termina siendo escrita como Id
. Yo uso copoint
para volver a convertirlo en un tipo List
.
En resumen, parece que la clave para utilizar el estado es tener funciones que devuelven una función modificar el estado y el valor verdadero resultado deseado ... responsabilidad: nunca he utilizado state
en el código de producción, tratando de obtener una sentir por eso.
información adicional sobre @ziggystar comentario
me dio por vencido en tratar usando stateT
puede ser otra persona puede mostrar si StateFreq
o StateRandom
se puede aumentar para realizar el cálculo combinado. Lo que encontré en cambio, es que la composición de los dos transformadores de estado se puede combinar de esta manera:
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
}
Se basa en g
que es una función de un parámetro tomando el resultado del primer transformador estado y devolver un transformador estado. A continuación, el siguiente funcionaría:
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]()))
¿No es la mónada 'State' no un" transformador de estado "en realidad? Y como segunda pregunta: ¿hay alguna forma más agradable de combinar los dados rodando y la suma en una única mónada de estado? ¿Cómo harías eso dadas las dos mónadas? – ziggystar
@ziggystar, técnicamente 'StateFreq' y' StateRandom' son mónadas. No creo que 'State [S, x]' sea un transformador de mónada ya que 'S' no necesita ser una mónada. Para una mejor manera de combinar, me pregunto también. No veo nada obviamente disponible. Puede ser 'stateT' podría ayudar, pero aún no lo he descubierto. – huynhjl
No escribí "transformador de mónada" sino "transformador de estado". Los objetos 'State [S, x] '' no tienen un estado sino una transformación de este último. Es solo que creo que el nombre podría ser elegido menos confuso. No se trata de tu respuesta, sino de Scalaz. – ziggystar