简体   繁体   English

如何在scalaz` \\ / notation中积累Throwable

[英]How to accumulate Throwable in scalaz` \/ notation

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. 我需要计算每个ab所有结果,如果有的话还要积累Throwable Is there any elegant way like Applicative style <*> ? 是否有任何优雅的方式,如Applicative样式<*>

UPDT: The result must be the following UPDT:结果必须如下

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] 结果列表有类型List[ List[Throwable] \\/ C]

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 C s. 假设你有一个类型Throwable \\/ A和另一个Throwable \\/ B的值列表,你想要将它们与函数(A, B) => Throwable \\/ C成对组合成一个累积错误列表或者是C的列表。 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. 在这里,我们将每对值转换为验证,使用应用tuple将它们组合在一起,同时累积错误,转换回析取,以便我们可以与f绑定,返回验证,以便我们可以使用traverseU应用程序,然后返回脱离。

(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.) (请注意,我假设列表具有相同的长度,或者您不介意忽略其中任何一个的额外结果,但这只是为了简单起见 - 如果您想要其他行为,则很容易调整。 )

Finally I have found an appropriate solution. 最后我找到了一个合适的解决方案。 The idea is to use monad transformers EitherT . 想法是使用monad变换器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. 在这里,我们从'a'获得所有“好”*值,并从'b'获得所有“好”值,就像通常的Applicative样式函数调用一样。 And no extra functions or extra data in result. 并且没有额外的功能或额外的数据。

'*' - term "good" means not 'Throwable' '*' - 术语“好”意味着不是'Throwable'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM