Combining validations with scalaz 7
Asked Answered
B

2

7

Given the following functions:

def foo( a: A ): ValidationNEL[String,Seq[B]] = ...

def bar( b: B ): ValidationNEL[String,C] = ...

I would like to combine them such as to build a function, which calls foo then eventually calls bar on each elements in the produced Seq, such a to get a ValidationNEL[String,Seq[C]]:

def fooAndBar( a: A ): ValidationNEL[String,Seq[C]]

Documentation in Scalaz 7 is very short and I could not find any relevant example.

Bulkhead answered 11/8, 2012 at 11:4 Comment(0)
L
5

Do a hardcore traversal on the B sequence. Note that I used List here since Scalaz 7 doesn't seem to have typeclass instances for Seq, however it should not be too hard to write your own if really needed.

import scalaz.{ValidationNEL, Traverse, NonEmptyList}
import scalaz.std.list.listInstance
case class A(a: Int)
case class B(b: Int)
case class C(c: Int)

def foo( a: A ): ValidationNEL[String,List[B]] = Validation.success(List(B(1), B(2)))
def bar( b: B ): ValidationNEL[String,C] = Validation.failure(NonEmptyList("error in " + b.b))//Validation.success(C(b.b * 2))

type ValNEL[A] = ValidationNEL[String, A]

def foobar(a: A): ValidationNEL[String, List[C]] =
  foo(a) flatMap { bs =>
    Traverse[List].traverse[ValNEL, B, C](bs)(bar)
  }

val r: scalaz.ValidationNEL[String, List[C]] = foobar(A(3))

Update: Also see the invaluable Haskellwiki Typeclassopedia

Lett answered 11/8, 2012 at 12:30 Comment(6)
Weird, flatMap does not seem to be a member of Validation. There is however a bind method. Should some implicit conversion take place ?Bulkhead
@Bulkhead I think it was renamed recently to flatMap (github.com/scalaz/scalaz/commit/…). I use 7-M1, has flatMap.Lett
I think if technically strict, Validation should not have flatMap/bind since it is not a Monad, but you could achieve the same by isoing to Either, binding that, and isoing back. The shortcut is convenient though.Lett
As a small side note: you can import scalaz.syntax.traverse._ and just write foo(a).flatMap(_.traverse[ValNEL, C](bar)).Homburg
Travis: Indeed. Also foo(a) flatMap (Traverse[List].traverse[ValNEL, B, C](_)(bar)) is also possible.Lett
Note, for the easily confused (like me) ValidationNEL has become ValidationNelChartography
I
1

Have a look at this gist using kleisli to do this with Either https://gist.github.com/3240574 and this with Validation https://gist.github.com/3230464

Interstratify answered 11/8, 2012 at 12:42 Comment(4)
Not a bad idea syntactically, but using the Kleisli >==> needs a ValidationNEL Bind instance in scope, which currently can be brought in by using Validation.validationMonad. This unfortunately overrides the default Applicative instance for ValidationNEL, so the traversal won't accumulate all errors :/Lett
Not sure why the downvote, the validation example can do this. When I get to a computer in a couple of days I'll produce an exact solution - unless I'm completely mistaken ;)Interstratify
Ah, crossed post. I think I get it.Interstratify
Having the custom Bind instance may solve it, but a bit too many annoyance for the syntactic ease (vs. just using flatMap)Lett

© 2022 - 2024 — McMap. All rights reserved.