This is similar to a trampoline. for loopSuccess, you provide an initial value, and a function which takes you to the next state. There are 3 possible next states:
X.left // stop processing with X as the result
Success(a).right // no result, run the next iteration with this value
Failure(e).right // stop processing, run the failure function on this result and return it
For loopFailure does the same thing with Failure and Success reversed, so you keep running until you return a left or a Success.
Here is a example of using loopSuccess:
import scalaz._
import Scalaz._
object TestLoopSuccess extends App {
// check if a number divides another, returning a Failure for division by zero
val divides : Int => Int => Validation[String,Boolean] = { div => num =>
if(div == 0) "division by zero".failure
else (num % div == 0).success
}
val allDivide : Int => List[Int] => String \/ Validation[Int,List[Int]] = { div => nums =>
nums match {
// empty list means we are done, so we return a left
case Nil => "All numbers divide".left
// process the head of the list and return a right
case x::xs => divides(div)(x).flatMap { divides =>
if(divides)
// head divides, so process more of the list
xs.success
else
// head does not divide, so we are done
"%d is not a multiple of %d".format(x,div).failure
}.right
}
}
println(Validation.loopSuccess(List(2,4,6,8).success[String], allDivide(0), identity[String])) // "division by zero"
println(Validation.loopSuccess(List(2,4,6,8).success[String], allDivide(2), identity[String])) // "All numbers divide"
println(Validation.loopSuccess(List(2,4,7,8).success[String], allDivide(2), identity[String])) // "7 is not a multiple of 2"
}