Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scalaz validation and ApplicativeBuilder limits

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?

like image 912
Digal Avatar asked Apr 21 '11 07:04

Digal


2 Answers

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)
like image 190
Tony Morris Avatar answered Nov 03 '22 12:11

Tony Morris


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)
    
like image 2
missingfaktor Avatar answered Nov 03 '22 10:11

missingfaktor