This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.bluecollar.scalaz | |
import scalaz.Scalaz._ | |
import scalaz._ | |
object StateMonadExamples extends App { | |
//Domain and test values | |
sealed trait Input | |
case object Coin extends Input | |
case object Turn extends Input | |
case class Machine(locked: Boolean, candies: Int, coins: Int) | |
private def applyInput(i: Input): (Machine) ⇒ Machine = | |
(m: Machine) => (i, m) match { | |
case (_, Machine(_, 0, _)) => m | |
case (Coin, Machine(false, _, _)) => m | |
case (Turn, Machine(true, _, _)) => m | |
case (Coin, Machine(true, candy, coin)) => Machine(false, candy, coin + 1) | |
case (Turn, Machine(false, candy, coin)) => Machine(true, candy - 1, coin) | |
} | |
val inputs = List(Coin, Turn, Coin, Turn, Coin, Turn, Coin, Turn) | |
val machine = Machine(true, 5, 10) | |
//Implementation with STATE MONAD starts ============= | |
val state = scalaz.StateT.stateMonad[Machine] | |
def rules(i: Input): State[Machine, (Int, Int)] = for { | |
_ <- modify(applyInput(i)) | |
m <- get | |
} yield (m.coins, m.candies) | |
def simulateMachine(inputs: List[Input]): State[Machine, (Int, Int)] = for { | |
_ <- state.sequence(inputs.map(rules)) | |
m <- get[Machine] | |
} yield (m.coins, m.candies) | |
def simulationWithStateMonad(inputs: List[Input], machine: Machine) = simulateMachine(inputs)(machine) | |
//Implementation with STATE MONAD ends ============= | |
//Implementation with foldLeft. A 2-(rather short)liner | |
def simulationWithFoldLeft(inputs: List[Input], machine: Machine) = inputs.foldLeft(machine) { (m, input) ⇒ | |
applyInput(input)(m) | |
} | |
//Implementation with foldLeft, where the result is the same format | |
def simulationWithFoldLeft2(inputs: List[Input], machine: Machine) = { | |
def convert(m: Machine) = (m, (m.coins, m.candies)) | |
inputs.foldLeft(convert(machine)) { (m, input) ⇒ | |
convert(applyInput(input)(m._1)) | |
} | |
} | |
//Test | |
println(simulationWithStateMonad(inputs, machine)) | |
//(Machine(true,1,14),(14,1)) | |
println(simulationWithFoldLeft(inputs, machine)) | |
//Machine(true,1,14)) | |
println(simulationWithFoldLeft2(inputs, machine)) | |
//(Machine(true,1,14),(14,1)) | |
} |
Kaloz has pointed out that the foldLeft evaluates the result immediately, whereas the simulateMachine returns a composable Monad, thus deferring the evaluation to a later point and leaving open the possibilities to compose the result with other monads.
ReplyDeleteHowever I'm still not convinced. With a slight modification of simulationWithFoldLeft (currying to the rescue) we can achieve something similar in a much simpler way:
def simulationWithFoldLeft3(inputs: List[Input])(machine: Machine): Machine = inputs.foldLeft(machine) { (m, input) ⇒
applyInput(input)(m)
}
//prepare the function
val run: Machine ⇒ Machine = simulationWithFoldLeft3(inputs)
//then apply it
println(run(machine))
//Machine(true,1,14)
I liked this article. Probably you will find it useful as well.
ReplyDeletehttps://softwarecorner.wordpress.com/2013/08/29/scalaz-state-monad/