I am starting to use the state monad to clean up my code. I have got it working for my problem where I process a transaction called CDR and modify the state accordingly. It is working perfectly fine for individual transactions, using this function to perform the state update.
def addTraffic(cdr: CDR): Network => Network = ...
Here is an example:
scala> val processed: (CDR) => State[Network, Long] = cdr =>
| for {
| m <- init
| _ <- modify(Network.addTraffic(cdr))
| p <- get
| } yield p.count
processed: CDR => scalaz.State[Network,Long] = $$Lambda$4372/1833836780@1258d5c0
scala> val r = processed(("122","celda 1", 3))
r: scalaz.State[Network,Long] = scalaz.IndexedStateT$$anon$13@4cc4bdde
scala> r.run(Network.empty)
res56: scalaz.Id.Id[(Network, Long)] = (Network(Map(122 -> (1,0.0)),Map(celda 1 -> (1,0.0)),Map(1 -> Map(1 -> 3)),1,true),1)
What i want to do now is to chain a number of transactions on an iterator. I have found something that works quite well but the state transitions take no inputs (state changes through RNG)
import scalaz._
import scalaz.std.list.listInstance
type RNG = scala.util.Random
val f = (rng:RNG) => (rng, rng.nextInt)
val intGenerator: State[RNG, Int] = State(f)
val rng42 = new scala.util.Random
val applicative = Applicative[({type l[Int] = State[RNG,Int]})#l]
// To generate the first 5 Random integers
val chain: State[RNG, List[Int]] = applicative.sequence(List.fill(5)(intGenerator))
val chainResult: (RNG, List[Int]) = chain.run(rng42)
chainResult._2.foreach(println)
I have unsuccessfully tried to adapt this, but I can not get they types signatures to match because my state function requires the cdr (transaction) input
Thanks
TL;DR
you can use
traversefrom theTraversetype-class on a collection (e.g.List) ofCDRs, using a function with this signature:CDR => State[Network, Long]. The result will be aState[Network, List[Long]]. Alternatively, if you don't care about theList[Long]there, you can usetraverse_instead, which will returnState[Network, Unit]. Finally, should you want to "aggregate" the resultsTas they come along, andTforms aMonoid, you can usefoldMapfromFoldable, which will returnState[Network, T], whereTis the combined (e.g. folded) result of allTs in your chain.A code example
Now some more details, with code examples. I will answer this using Cats
Staterather than Scalaz, as I never used the latter, but the concept is the same and, if you still have problems, I will dig out the correct syntax.Assume that we have the following data types and imports to work with:
As it is clear, the
Positionrepresents a point in a 2D plane and aMovecan move such point up, down, left or right.Now, lets create a method that will allow us to see where we are at a given time:
and a method to change our position, given a
Move:Notice that this will return a
String, with the name of the move followed by an exclamation mark. This is just to simulate the type change fromMoveto something else, and show how the results will be aggregated. More on this in a bit.Now let's try to play with our methods:
And we can feed it an initial
Positionand see the result:(you can ignore the
.valuethere, it's a quirk due to the fact thatState[S,A]is really just an alias forStateT[Eval,S,A])As you can see, this behaves as you would expect, and you can create different "blueprints" (e.g. sequences of state modifications), which will be applied once an initial state is provided.
Now, to actually answer to you question, say we have a
List[Move]and we want to apply them sequentially to an initial state, and get the result: we usetraversefrom theTraversetype-class.Alternatively, should you not need the
Aat all (theListin you case), you can usetraverse_, instead oftraverseand the result type will be:Finally, if your
Atype inState[S,A]forms aMonoid, then you could also usefoldMapfromFoldableto combine (e.g. fold) allAs as they are calculated. A trivial example (probably useless, because this will just concatenate allStrings) would be this:Whether this final approach is useful or not to you, really depends on what
Ayou have and if it makes sense to combine it.And this should be all you need in your scenario.