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.

paradigmatic
  • 40,153
  • 18
  • 88
  • 147

2 Answers2

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

ron
  • 9,262
  • 4
  • 40
  • 73
  • Weird, `flatMap` does not seem to be a member of `Validation`. There is however a `bind` method. Should some implicit conversion take place ? – paradigmatic Aug 11 '12 at 12:46
  • @paradigmatic I think it was renamed recently to flatMap (https://github.com/scalaz/scalaz/commit/d1aabf31f8faa6fb6ef8dc172efdf68b945a5216). I use 7-M1, has flatMap. – ron Aug 11 '12 at 13:00
  • 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. – ron Aug 11 '12 at 13:03
  • 1
    As a small side note: you can import `scalaz.syntax.traverse._` and just write `foo(a).flatMap(_.traverse[ValNEL, C](bar))`. – Travis Brown Aug 11 '12 at 17:52
  • Travis: Indeed. Also `foo(a) flatMap (Traverse[List].traverse[ValNEL, B, C](_)(bar))` is also possible. – ron Aug 11 '12 at 19:20
  • Note, for the easily confused (like me) ValidationNEL has become ValidationNel – Gavin May 08 '13 at 09:27
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

Channing Walton
  • 3,977
  • 1
  • 30
  • 59
  • 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 :/ – ron Aug 11 '12 at 12:57
  • 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 ;) – Channing Walton Aug 11 '12 at 12:57
  • Ah, crossed post. I think I get it. – Channing Walton Aug 11 '12 at 12:58
  • Having the custom Bind instance may solve it, but a bit too many annoyance for the syntactic ease (vs. just using flatMap) – ron Aug 11 '12 at 13:01