Skip to content

Instantly share code, notes, and snippets.

@channingwalton
Created May 31, 2012 21:28
Show Gist options
  • Save channingwalton/2846428 to your computer and use it in GitHub Desktop.
Save channingwalton/2846428 to your computer and use it in GitHub Desktop.
State Monad and Validation with Scalaz
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))
*/
@channingwalton
Copy link
Author

Please don't apologise, you are being very helpful. I think I'll look at scalaz 7.

@YoEight
Copy link

YoEight commented Jun 5, 2012

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

@channingwalton
Copy link
Author

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