10
votes

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?

1

1 Answers

10
votes

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.