10

We're using scalaz validation trait in our project to validate HTTP parameters. The common case is taking few validated values and performing neccessary action only if all of them are valid, returning list of errors otherwise:

(pavam1Val.liftFailNel |@|
 param2Val.liftFailNel |@|
 param3Val.liftFailNel) {
    getSomeResponse(_, _, _)
}

This works nice, until we have to use more than 8 parameters, because |@| operator constructs ApplicativeBuilder, which is limited to 8 arguments. Is there another way to perform such all-at-once validation, preferably keeping the code readable?

Digal
  • 247
  • 2
  • 10

2 Answers2

11

you want to use the <*> method, along with a single call to map (or if you prefer). You can keep using <*> indefinitely.

scala> val param1Val = success[String, Int](7)                              
param1Val: scalaz.Validation[String,Int] = Success(7)

scala> val param2Val = failure[String, Int]("abc")                          
param2Val: scalaz.Validation[String,Int] = Failure(abc)

scala> val param3Val = success[String, Int](9)                              
param3Val: scalaz.Validation[String,Int] = Success(9)

scala> val r = param1Val <*> (param2Val <*> (param3Val map getSomeResponse))
r: scalaz.Validation[String,Int] = Failure(abc)
Tony Morris
  • 3,045
  • 2
  • 29
  • 17
  • 2
    Don't forget that `getSomeResponse` needs to be a curried function, that is `Int => Int => Int => Int`. – retronym Apr 23 '11 at 06:18
  • 2
    Don't we have there the inversed order of arguments? I mean to get the same result as in the question we need to write `param3Val <*> (param2Val <*> (param1Val map getSomeResponse))`. – CheatEx Apr 26 '11 at 09:42
2

A couple more ways to do it:

  1. Lift the relevant function to Validation context, and then apply it to the values.

    getSomeResponse.lift[({ type L[X] = Validation[Y, X] })#L] apply (
      param1Val, param2Val, param3Val
    )
    
  2. Use monad comprehension.

    for {
      x1 <- param1Val
      x2 <- param2Val
      x3 <- param3Val
    } yield getSomeResponse(x1, x2, x3)
    
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • 3
    Of course the monadic version won't accumulate errors just bail out on the first one. – ron Aug 11 '12 at 06:56
  • While here, the lifting would use the Functor instance, so it bails out too. – ron Aug 11 '12 at 07:59
  • ron, No. Lifting on arity 1 function uses Functor instance. Lifting on functions with greater arity uses Applicative instance. So it won't bail out. – missingfaktor Aug 11 '12 at 14:16