Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compose Scalaz validations

Tags:

scala

scalaz

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?

like image 892
chrsan Avatar asked Feb 24 '12 09:02

chrsan


1 Answers

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.

like image 68
missingfaktor Avatar answered Nov 06 '22 10:11

missingfaktor