Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flattening Nested Scalaz Validations

Tags:

scala

scalaz

I'm fairly new to scalaz and I've started out with validations.

I have some validation functions of the form:

def validateXyz(...): ValidationNEL[String, String] = ...

I'm then using the applicative style to combine multiple validations and then call another function that also returns a validation:

(validateXyz(...) |@| validateAbc(...)) { (first, second) =>
   otherFunction(first, second)
}

where,

def otherFunction(first: String, second: String): ValidationNEL[String, String] = ...

However, when calling the above the resulting type is:

val result: ValidationNEL[String, ValidationNEL[String, String]] = ...

I can unpack this by calling fold on the result with two functions, the first which just propagates the NEL as a fail and the second which just propagates its argument:

def propagateF(result: NonEmptyList[String]): ValidationNEL[String, String] = result.fail
def propagateV(result: ValidationNEL[String, String]) = result

result.fold(propagateF, propagateV)
// result type: ValidationNEL[String, String]

This works and returns the correct types and results. However it doesn't feel like the correct solution so I must be missing something. What do I need to do to avoid this horrible fold at the end?

like image 563
Chris Turner Avatar asked Jun 23 '12 22:06

Chris Turner


1 Answers

What you are looking for here is monadic join.

The thing is that Validation itself is not really a monad, since the error side carries a Semigroup structure that cannot be preserved by Monad. But you can always drop down into the Either monad if you need to. This functionality is provided by flatMap.

(validateXyz(...) |@| validateAbc(...))(otherFunction).flatMap(x => x)

If you have an error on the outside, the result will be that error. If you have an error inside of a success, the result will be the inner error. Otherwise the result will be a success. Note the impossibility of having an error both on the inside and outside. This is why you have to use Applicative rather than Monad if you want to combine errors.

like image 145
Apocalisp Avatar answered Nov 04 '22 03:11

Apocalisp