Question

Cannot figure out if it is possible to write something like this using Scalaz 7. I have tried to express myself with the comments inside the code block.

def validate1(p: String) = ValidationNel[String, Value] = ...
def validate2(p: String) = ValidationNel[String, Value] = ...

validateCombination(p1: String, p2: String) = {
  // I would like to write something like
  (validate1(p1) |@| validate2(p2)) { (v1, v1) =>
    // And validate the combinations here and return successNel of failNel
  }
}

def validate(p1: String, p2: String, p3: String) = {
  (validateCombination(p1, p2) |@| validate1(p3)) { (v1, v2, v3) =>
    // Notice the three parameters I want to have here
  }
}

I just end up with different types of confusing compilation errors in validateCombinations or just 2 parameters for the applicative functor I get inside validate function, one of them being of type ValidationNel[...].

Was it helpful?

Solution

You could use .flatMap(identity) in method validateCombination to produce ValidationNel[String, (Value, Value)] and pattern matching in method validate like this:

def validateCombination(p1: String, p2: String): ValidationNel[String, (Value, Value)] = {
  // I would like to write something like
  (validate1(p1) |@| validate2(p2)) { (v1, v1) =>
    (v1, v2).successNel[String]
  }.flatMap(identity)
}

def validate(p1: String, p2: String, p3: String) = {
  (validateCombination(p1, p2) |@| validate1(p3)) { case ((v1, v2), v3) =>
    // Notice the three parameters I want to have here
  }
}

flatMap(identity)

Normally you would use method flatten on nested containers to get M[T] from M[M[T]]. It works on Future, Option, Try, collections and so on.

In this case type M[T] = ValidationNel[String, T].

I don't know why there is no method flatten in Validation, but you could always use flatMap(identity) instead of flatten.

match

As Ben James noted, flatMap on Validation is dubious. You could always use match instead of it:

(validate1(p1) |@| validate2(p2)) { (v1, v1) =>
  (v1, v2).successNel[String]
} match {
  case Success(s) => s
  case Failure(f) => Failure(f)
}

pattern matching

Pattern matching is the common way to deal with tuples. For instance it's vary useful with foldLeft method, like foldLeft(1 -> 2){ case ((a, b), c) => ??? }.

If you find yourself using getters _N on Tuple you are probably should use pattern matching.

for comprehension

As Daniel C. Sobral noted for comprehension could be easier to understand.

You could use it in your validate method like this:

def validate(p1: String, p2: String, p3: String) = {
  for{
    (v1, v2) <- validateCombination(p1, p2) // pattern matching
    v3 <- validate1(p3)
  } yield ??? // Your code here
}

It involves pattern matching without case keyword.

Note that for comprehension calls flatMap on validateCombination(p1, p2), so you'll lost error messages from validate1(p3) in case validateCombination(p1, p2) is Failure. On the contrary, |@| collects all error messages from both sides.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top