簡體   English   中英

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

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

我們假設我有兩個計算結果列表

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

我有計算最終結果的功能,如

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

我需要計算每個ab所有結果,如果有的話還要積累Throwable 是否有任何優雅的方式,如Applicative樣式<*>

UPDT:結果必須如下

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

我遇到的最好的解決方案是

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(_))
  }

然后

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

但在那種情況下,我有一些額外的結果。 結果列表有類型List[ List[Throwable] \\/ C]

我不確定我是否完全理解這個問題,但我會對它進行一次嘗試,希望能夠足夠接近你的推斷。 假設你有一個類型Throwable \\/ A和另一個Throwable \\/ B的值列表,你想要將它們與函數(A, B) => Throwable \\/ C成對組合成一個累積錯誤列表或者是C的列表。 我會寫這樣的:

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] = ???

我們有很多方法可以實現這一點,但基本的想法是我們需要暫時將析取轉換為驗證,以便獲得我們想要的錯誤累積(請參閱我的問題以便討論為什么這是必要的) 。

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

在這里,我們將每對值轉換為驗證,使用應用tuple將它們組合在一起,同時累積錯誤,轉換回析取,以便我們可以與f綁定,返回驗證,以便我們可以使用traverseU應用程序,然后返回脫離。

(請注意,我假設列表具有相同的長度,或者您不介意忽略其中任何一個的額外結果,但這只是為了簡單起見 - 如果您想要其他行為,則很容易調整。 )

最后我找到了一個合適的解決方案。 想法是使用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] = ...

接下來是魔術:

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

在這里,我們從'a'獲得所有“好”*值,並從'b'獲得所有“好”值,就像通常的Applicative樣式函數調用一樣。 並且沒有額外的功能或額外的數據。

'*' - 術語“好”意味着不是'Throwable'

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM