Suppose I need to write a validating function Validate[A]
:
type Status[A] = Validation[List[String], A]
type Validate[A] = A => Status[A] // should be Kleisli
The function returns either a Success
with the input if the input is valid or a Failure
with the list of errors if it is not.
For example,
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 Validation
is a semigroup Validate
is semigroup too and (if I define it as Kleisli
) I can compose validating functions as follows:
val isEvenPositive = isEven |+| isPositive
Suppose now that I need to validate X
:
case class X(x1: Int, // should be positive
x2: Int) // should be even
Since Validation
is an applicative functor Validate
is an applicative functor too.
val x: Validate[X] = (isPositive |@| isEven)(X.apply)
Does it make sense ?