1

Let's assume that I have two lists of calculation results

val a: List[ Throwable \/ A] = ...
val b: List[ Throwable \/ B] = ...

and I have function which is calculating final result such as

def calc(a: A, b: B): Throwable \/ C = ...

I need to calculate all results for every a and b and also to accumulate Throwable if there are some. Is there any elegant way like Applicative style <*> ?

UPDT: The result must be the following

val c: List[ Throwable \/ C] ..

The best solutuion that I came to is

def combine[A, B, C](f: (A, B) => Throwable \/ C, a: Throwable \/ A, b: Throwable \/ B): List[Throwable] \/ C = (a, b) match{
    case ( -\/(errA), \/-(_)) => -\/(List(errA))
    case (\/-(_), -\/(errB)) => -\/(List(errB))
    case (-\/(errA), -\/(errB)) => -\/(List(errA, errB))
    case (\/-(valA), \/-(valB)) => f(valA, valB).leftMap( List(_))
  }

And than

val result = (a |@| b)(combine(calc, _, _))

But in that case I have some extra results. And result list has type List[ List[Throwable] \/ C]

ponkin
  • 2,363
  • 18
  • 25

2 Answers2

3

I'm not sure I exactly understand the question, but I'll take a stab at it and hopefully will be close enough for you to extrapolate. Suppose you've got a list of values of type Throwable \/ A and another of Throwable \/ B and you want to combine them pairwise with a function (A, B) => Throwable \/ C into either a list of accumulated errors or a list of Cs. I'll write that like this:

import scalaz._, Scalaz._

def process[A, B, C](
  as: List[Throwable \/ A],
  bs: List[Throwable \/ B]
)(f: (A, B) => Throwable \/ C): NonEmptyList[Throwable] \/ List[C] = ???

There are lots of ways we could implement this, but the basic idea is that we'll need to convert the disjunctions temporarily into validations in order to get the error accumulation we want (see my question here for some discussion of why this is necessary).

def process[A, B, C](
  as: List[Throwable \/ A],
  bs: List[Throwable \/ B]
)(f: (A, B) => Throwable \/ C): NonEmptyList[Throwable] \/ List[C] =
  as.zip(bs).traverseU {
    case (a, b) =>
      val validatedA: ValidationNel[Throwable, A] = a.validation.toValidationNel
      val validatedB: ValidationNel[Throwable, B] = b.validation.toValidationNel

      validatedA.tuple(validatedB).disjunction.flatMap {
        case (a, b) => f(a, b).leftMap(NonEmptyList(_))
      }.validation
  }.disjunction

Here we convert each pair of values to validations, use the applicative tuple to combine them while accumulating errors, convert back to a disjunction so that we can bind with f, back to a validation so that we can sequence applicatively with traverseU, and then back to a disjunction.

(Note that I'm assuming that the lists have the same length or that you don't mind ignoring extra results in either one, but that's just for the sake of simplicity—it'd be easy to adjust if you wanted other behavior.)

Community
  • 1
  • 1
Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • The idea is to combine all "good" values from `a` with all "good" values from `b` in function `calc` so the result will be the `List[Throwable \/ C]` and append all "bad" values from `a` and all "bad" values from `b` to the final result List. – ponkin Dec 06 '15 at 07:42
0

Finally I have found an appropriate solution. The idea is to use monad transformers EitherT.

val a: List[Throwable \/ A] = ...
val b: List[Throwable \/ B] = ...
val aT = EitherT.eitherT(a)
// aT: scalaz.EitherT[List,Throwable, A] = ...
val bT = EitherT.either(b)
// bT: scalaz.EitherT[List,Throwable, B] = ...

Next comes the magic:

val result = (aT |@| bT)(calc( _, _ ))
// c: scalaz.EitherT[[+A]List[A],Throwable,C] = ...

Here we get all "good"* values from 'a' and all "good" values from 'b' like usual Applicative style function call. And no extra functions or extra data in result.

'*' - term "good" means not 'Throwable'

ponkin
  • 2,363
  • 18
  • 25