I would like to use Scalaz for validations and like to be able to reuse the validation functions in different contexts. I'm totally new to Scalaz btw.
Let's say I have these simple checks:
def checkDefined(xs: Option[String]): Validation[String, String] =
xs.map(_.success).getOrElse("empty".fail)
def nonEmpty(str: String): Validation[String, String] =
if (str.nonEmpty) str.success else "empty".fail
def int(str: String): Validation[String, Int] = ...
I like to be able to compose validations where output from one is fed into the other. I could easily do that with flatMap
or via for comprehensions but it feels like there must be a better way than that.
for {
v1 <- checkDefined(map.get("foo"))
v2 <- nonEmpty(v1)
v3 <- int(v2)
v4 <- ...
} yield SomeCaseClass(v3, v4)
or
val x1 = checkDefined(map get "foo").flatMap(nonEmpty).flatMap(int)
val x2 = check(...)
// How to combine x1 and x2?
Any thoughts from the Scalaz experts out there?
In addition to the solutions suggested by @oxbow_lakes, you can also use Kleisli composition.
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> def f: Int => Validation[String, Int] = i => if(i % 2 == 0) Success(i * 2) else Failure("Odd!")
f: Int => scalaz.Validation[String,Int]
scala> def g: Int => Validation[String, Int] = i => if(i > 0) Success(i + 1) else Failure("Not positive!")
g: Int => scalaz.Validation[String,Int]
scala> type Va[+A] = Validation[String, A]
defined type alias Va
scala> import Validation.Monad._
import Validation.Monad._
scala> kleisli[Va, Int, Int](f) >=> kleisli[Va, Int, Int](g)
res0: scalaz.Kleisli[Va,Int,Int] = scalaz.Kleislis$$anon$1@4fae3fa6
scala> res0(11)
res1: Va[Int] = Failure(Odd!)
scala> res0(-4)
res2: Va[Int] = Failure(Not positive!)
scala> res0(4)
res3: Va[Int] = Success(9)
A function of type A => M[B]
where M : Monad
is called a Kleisli arrow.
You can compose two Kleisli arrows A => M[B]
and B => M[C]
to get an arrow A => M[C]
using >=>
operator. This is known as Kleisli composition.
The expression kleisli(f) >=> kleisli(g) >=> kleisli(h)
is equivalent to x => for(a <- f(x); b <- g(a); c <- h(b)) yield c
, minus the unnecessary local bindings.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With