Nested validations

88 views
Skip to first unread message

Normen Müller

unread,
May 7, 2013, 3:03:48 AM5/7/13
to sca...@googlegroups.com
Hi, I got stuck ... need help.

Is there a nice way to resolve the following code snippet, such that `result` directly conforms to `f`'s result type:

// for investigation
type I; type O; type A; type B; type C; type D
  
val f: I => ValidationNEL[String,O] = i => {
  val va: ValidationNEL[String, A] = ??? // <computation based on `i`>
  val vb: ValidationNEL[String, B] = ??? // <computation based on `i`>
  // 1st evaluation
  val result: ValidationNEL[String, ValidationNEL[String, O]] =
    (va |@| vb)((a, b) => { 
      val vc: ValidationNEL[String, C] = ??? //<computation based on `a`>
      val vd: ValidationNEL[String, D] = ??? //<computation based on `b`>
      // 2nd evaluation
      (vc |@| vd)((c, d) => ???.asInstanceOf[O]) // <final computation>        
    })
  result // <<< !!!
}

Cheers, /nm

Stefan Hoeck

unread,
May 7, 2013, 3:27:05 AM5/7/13
to sca...@googlegroups.com
Hey Normen

Is the computation of vc only based on a (and not on b) and the computation of vd only based on b (and not on a)? If yes, you could do the following:

  type ValRes[+A] = ValidationNEL[String,A]
  type DisRes[+A] = NonEmptyList[String] \/ A

  def iToA(i: I): DisRes[A] = ...
  def aToC(a: A): DisRes[C] = ...

  def iToB(i: I): DisRes[B] = ...
  def bToD(b: B): DisRes[D] = ...

  def calcO(c: C, d: D): O = ...

  val f: I => ValRes[O] = i =>
    (iToA(i) >>= aToC validation) |@| (iToB(i) >>= bToD validation) apply calcO

Note how we use the Disjunction Monad for sequential validation and the Validation Applicative for error accumulation.

If on the other hand you only want to transform 'result' in your example to a ValidationNEL[String,O] you can do the following:

  result flatMap identity

Cheers, Stefan

Edmondo Porcu

unread,
May 7, 2013, 3:28:29 AM5/7/13
to sca...@googlegroups.com
Hello Stefan,
can you please clarify which impots are needed?

Thanks :)


2013/5/7 Stefan Hoeck <efasc...@gmail.com>
--
You received this message because you are subscribed to the Google Groups "scalaz" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scalaz+un...@googlegroups.com.
To post to this group, send email to sca...@googlegroups.com.
Visit this group at http://groups.google.com/group/scalaz?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Tony Morris

unread,
May 7, 2013, 3:35:17 AM5/7/13
to sca...@googlegroups.com

There are methods on Validation to treat it as if it were \/.

--

Stefan Hoeck

unread,
May 7, 2013, 3:36:18 AM5/7/13
to sca...@googlegroups.com
I'm afraid I still use the monster import

  import scalaz._, Scalaz._

in almost all of my code.

However, this should work as well:

  import scalaz.{\/, NonEmptyList, Validation, ValidationNel}
  import scalaz.syntax.monad._

Note that I used scalaz 7.0.0.

Cheers, Stefan

Stefan Hoeck

unread,
May 7, 2013, 3:44:10 AM5/7/13
to sca...@googlegroups.com
Yeah, I know. I usually tend to distinguish clearly between \/ and Validation in my code, but you could of course also use Validation.flatMap instead of going via \/ in my example.

By the way, wasn't there a discussion some time ago about renaming Validation.flatMap to something else (bind?) since Validation does not define a Monad?

Cheers, Stefan

Normen Müller

unread,
May 7, 2013, 6:55:02 AM5/7/13
to sca...@googlegroups.com
Hi Stefan,

first of all thanks for your answer!


On Tuesday, May 7, 2013 9:44:10 AM UTC+2, Stefan Hoeck wrote:
Yeah, I know. I usually tend to distinguish clearly between \/ and Validation in my code, but you could of course also use Validation.flatMap instead of going via \/ in my example.

As I am still used to ScalaZ 6.0.4, could you /please/ post your solution without \/ but with Validation only? Thanks, /nm

Stefan Hoeck

unread,
May 7, 2013, 7:14:12 AM5/7/13
to sca...@googlegroups.com
Hi Normen
The following should work with scalaz 6 and 7 except that in 7 it is 'ValidationNel':


  type ValRes[+A] = ValidationNEL[String,A]

  def iToA(i: I): ValRes[A] = ...
  def aToC(a: A): ValRes[C] = ...

  def iToB(i: I): ValRes[B] = ...
  def bToD(b: B): ValRes[D] = ...


  def calcO(c: C, d: D): O = ...

  val f: I => ValRes[O] = i =>
    (iToA(i) flatMap aToC) |@| (iToB(i) flatMap bToD) apply calcO

If your validation functions (what I call iToA and so on) have different signatures (additional parameters, especially of type I, O, A, B, C, or D) the solution might look different. In that case I'd need at least their signature to come up with some working code.

Cheers, Stefan

Normen Müller

unread,
May 7, 2013, 9:36:50 AM5/7/13
to sca...@googlegroups.com
Hi Stefan, really, thank you very much!

Unfortunately currently I have to switch task :-( Will asap investigate if this solution solves my problem... in other words, if the signatures fit. So, maybe, will recontact you ;-) 

Thanks again and I'll give you feedback as soon as I have completed my investigation. Cheers, /nm

Normen Müller

unread,
May 13, 2013, 5:07:02 AM5/13/13
to sca...@googlegroups.com
Hi Stefan, the signatures fit, basically. Thanks! It's not exactly what I want, though: due to performance issues I must have some computations to come first and only if all of those succeed I want to continue with the others. 

Regarding your proposal `(iToA(i) flatMap aToC) |@| (iToB(i) flatMap bToD) apply calcO`, I first must check `iToAand `iToB` which are very fast checks. If one of those fails then "stop". If both succeed then go on with the more expensive computations, e.g. `aToC`.

So I came up with the following:

type I = String; type O; type A = String; type B = String; type C; type D
type ValRes[+A] = ValidationNel[String,A]
  
def iToA(i: I): ValRes[A] = "a".success // "iToA failed".failNel
def aToC(a: A): ValRes[C] = "aToC failed".failNel
     
def iToB(i: I): ValRes[B] = "b".success // "iToB failed".failNel
def bToD(b: B): ValRes[D] = "bToD failed".failNel
 
def calcO(c: C, d: D): O = ???

val f: I => ValRes[O] = i =>
   ((iToA(i) |@| iToB(i)).tupled.flatMap { case (a,b) =>
     (aToC(a) |@| bToD(b)) apply calcO
   }).fold(
     (es: NonEmptyList[String]) => {
       logger.error(s"""Invalid params:\n${es.list mkString "\n"}""")
       es.failure
     },
     (o: O) => {
       logger.info("Success")
       o.success
     })

Is `tupled` the right direction here?

I guess actually my question is the following: how to control nested validations and accumulate all errors along the way? For example, I have a couple of validations v1, ..., vn which I want to check at the very beginning. Then I have another couple of validations w1, ..., wm which I want to check if v1, ..., vn succeeded. Then I have another couple of validations ... . If `tupled` is the right direction, than the pattern could be:

((v1 |@| v2 |@| ... |@| vn).tupled.flatMap { case (v1, ..., vn) =>
  ((w1 |@| w2 |@| ... |@| wm)).tupled.flatMap { case (w1, ..., wm) => 
     // if all validations succeed
     doFinalComputation(): Result
  }
}).fold(
  (allError: NonEmptyList[String]) => {
    logger.error(s"""Invalid params:\n${allError.list mkString "\n"}""")
    allError.failure
  },
  (r: Result) => {
    logger.info("Success")
    r.success
  })

What do you think? Cheers, /nm 

On Tuesday, May 7, 2013 1:14:12 PM UTC+2, Stefan Hoeck wrote:

Stefan Höck

unread,
May 15, 2013, 11:30:01 PM5/15/13
to sca...@googlegroups.com
Hey Normen
Sorry for not replying earlier. Your code looks fine and I don't know of a 'nicer' solution for your use case so go ahead with it!

Cheers, Stefan
Reply all
Reply to author
Forward
0 new messages