Created
May 31, 2012 21:28
-
-
Save channingwalton/2846428 to your computer and use it in GitHub Desktop.
State Monad and Validation with Scalaz
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
import scalaz._ | |
import Scalaz._ | |
/** | |
* Use the state monad to 'process a trade' and store the new trade. | |
* As well as processing the trade, handle validation errors. | |
*/ | |
object StateMonad extends App { | |
case class Trade(info: String) | |
type Store = List[Trade] | |
// a function that takes a new trade and processes it yielding new state and validation | |
val newTrade = (newTrade: Trade) ⇒ | |
for ( | |
_ ← log("New Trade"); | |
accepted ← accept(newTrade); | |
_ ← log("Hedging New Trade"); | |
hedged ← hedge(newTrade); | |
_ ← log("Validating portfolio"); | |
portfolio ← validatePortfolio; | |
_ ← log("New trade processed") | |
) yield (accepted.liftFailNel |@| hedged.liftFailNel |@| portfolio.liftFailNel) {_ + _ + _} | |
println("Haven't done anything yet!") | |
// assume some existing state | |
var globalState: Store = Nil | |
// exercise the newTrade function with the existing state | |
val (newState, validation) = newTrade(Trade("Big Trade"))(globalState) | |
// assign the new state to our global state if the validation says its ok | |
globalState = validation.fold(failures ⇒ { println(failures); globalState }, msg ⇒ newState) | |
println("Store contains " + globalState) | |
// does nothing but print a messages and return the state its given | |
def log(m: String) = state[Store, Unit](s ⇒ (s, println(m))) | |
// accepts a trade putting it into the store | |
def accept(newTrade: Trade) = state[Store, Validation[String, String]](s ⇒ (newTrade :: s, "trade accepted".success)) | |
// hedge against the new trade - apparently its all the rage | |
def hedge(against: Trade) = state[Store, Validation[String, String]](s ⇒ (Trade("Hedge Trade against " + against) :: s, "hedge trade step".success)) | |
// validate the portfolio doing nothing with the state | |
def validatePortfolio = state[Store, Validation[String, String]](s ⇒ { | |
if (s.size > 10) (s, "Portolio is too big".fail) | |
else (s, "All ok".success) | |
}) | |
} | |
/* | |
Running this produces: | |
Haven't done anything yet! | |
New Trade | |
Hedging New Trade | |
Validating portfolio | |
New trade processed | |
Store contains List(Trade(Hedge Trade against Trade(Big Trade)), Trade(Big Trade)) | |
*/ |
I Think you should use stateful computation as
S => Validation[E, (S, A)] // like a monad transformer
not like
S => (s, Validation[E, A])
Like so you'll sure your state can't be modified if your previous validation is a failure.
You just need those methods
def modifyM[F[_], S](f: S => S)(implicit: Pure[F]): StateT[F, S, Unit] = stateT(s => (f(s), ()).pure[F])
def liftM[F[_], S, A](fa: F[A])(implicit: Functor[F]): StateT[F, S, A] = stateT(s => fa.map(a => (s, a)))
def initM[F[_], S, S](implicit P: Pure[F]): StateT[F, S, S] = stateT(s => (s, s).pure[F])
newTrade becomes
type VS[A] = Validation[String, A]
val newTrade: (newTrade: Trade) => for {
_ <- modifyM[VS, Store](newTrade :: _) // accepted
_ <- modifyM[VS, Store](Trade("Hedge Trade against " + newTrade) :: _) //hedge
s <- initM[VS, Store]
r <- if (s.size > 10) liftM[VS, String]("Portolio is too big".fail)
else liftM[VS, String]("All ok".success)
} yield r
Thanks, I was thinking of something similar. This really helps :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Please don't apologise, you are being very helpful. I think I'll look at scalaz 7.