Writing Validation Functions in Scala

Suppose I need to write a validation function Validate[A]:

type Status[A] = Validation[List[String], A]
type Validate[A] = A => Status[A] // should be Kleisli

The function returns either Successwith input if the input is valid, or Failurewith a list of errors if it is not.

For instance,

val isPositive: Validate[Int] = {x: Int =>
  if (x > 0) x.success else List(s"$x is not positive").failure 
}

val isEven: Validate[Int] = {x: Int =>
  if (x % 2 == 0) x.success else List(s"$x is not even").failure
}

Since Validationa semigroup is Validatealso a semigroup and (if I define it as Kleisli), I can compose the checking functions as follows:

val isEvenPositive = isEven |+| isPositive

Suppose now that I need to check X:

case class X(x1: Int, // should be positive 
             x2: Int) // should be even

Since it Validationis an applicative functor, Validateit is also an applicative functor.

val x: Validate[X] = (isPositive |@| isEven)(X.apply) 

Does it make sense?

+4
source share
1 answer

, . , type ValidationNel[L, R] = Validation[NonEmptyList[L], R], . , aValidation.toValidationNel . , .

def foo: ValidationNel[String, Int]
def bar: ValidationNel[String, Double]
val composed: ValidationNel[String, Double] = foo(input) |@| bar(input) apply { (i: Int, d: Double) => i * d }

(V Validation)

(A => V[L, B], A => V[L, C]) => (A => V[L, (B, C)])

. , , , .

, , .

  def composeV[A, B, C, L: Semigroup](f: A => Validation[L, B], g: A => Validation[L, C]): A => Validation[L, (B, C)] = {
    import scalaz.syntax.arrow._
    import scalaz.syntax.apply._
    import scalaz.std.function.function1Instance
    val oneInput: (A) => (Validation[L, B], Validation[L, C]) = f &&& g
    oneInput andThen {
      case (vb, vc) =>
        vb |@| vc apply { case x: (B, C) => x }
    }
  }

:

def composeV[A, B, C, L: Semigroup](f: A => Validation[L, B], g: A => Validation[L, C]): A => Validation[L, (B, C)] = {
  import scalaz.syntax.apply._
  import scalaz.std.function.function1Instance
  f |@| g apply { case (vb, vc) =>
    vb |@| vc apply { case x: (B, C) => x }
  }
}
+1

All Articles