Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compose functions that return Validation?

This is a follow-up to my previous question

Suppose I have two validating functions that return either the input if it is valid or the error messages if it is not.

type Status[A] = ValidationNel[String, A]

val isPositive: Int => Status[Int] = 
  x => if (x > 0) x.success else s"$x not positive".failureNel

val isEven: Int => Status[Int] = 
  x => if (x % 2 == 0) x.success else s"$x not even".failureNel

Suppose also that I need to validate an instance of case class X:

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

More specifically I need a function checkX: X => Status[X]. Moreover, I'd like to write checkX as a composition of isPositive and isEven.

val checkX: X => Status[X] =
  ({x => isPositive(x.x1)} |@| {x => isEven(x.x2)}) ((X.apply _).lift[Status])

Does it make sense ?
How would you write checkX as a composition of isPositive and isEven?

like image 221
Michael Avatar asked May 23 '26 20:05

Michael


1 Answers

There are lots of ways to write this, but I like the following:

val checkX: X => Status[X] = x => isPositive(x.x1).tuple(isEven(x.x2)).as(x)

Or:

val checkX: X => Status[X] =
  x => isPositive(x.x1) *> isEven(x.x2) *> x.point[Status]

The key point is that you want to run the two validations only for their "effects" and then return the original value in the new context. This is a perfectly legitimate applicative operation, as your own implementation shows. There are just some slightly nicer ways to write it.

like image 76
Travis Brown Avatar answered May 25 '26 11:05

Travis Brown