2015-05-28 163 views
1

我正在做一些可能會失敗的計算,所以我使用了析取類型作爲結果。我有我已經寫了下面的功能相當常見的模式:如何使用組合器嵌入分離器的作家monad摺疊序列?

def traverse[A, B, E](a: Iterable[A], b: B)(f: (B, A) => E \/ B): E \/ B = 
    a.foldLeft[E\/B](\/-(b)){case (b0, a0) => b0 flatMap (f(_, a0))} 

但現在我要記錄的計算(均以失敗或成功的情況下)。我需要一個以下類型的函數:

type Logged[T] = scalaz.Writer[String,T] 
def traverseLogged[A, B, E] 
     (a: Iterable[A], lb: Logged[B]) 
     (f: (Logged[B], A) => Logged[E \/ B]): Logged[E \/ B] 

但是我找不到依賴於combinators的「很好的實現」。我想出了以下實現:

a.foldLeft[Logged[E \/ B]](lb.map(_.right[E])){ 
    (lb0, a0) => 
    val (log, value) = lb0.run 
    value match { 
     case -\/(err) => lb0 
     case \/-(b) => f(b.set(log),a0) 
    } 
} 

有沒有一種方法來實現它,而不必運行記錄的b?

PS:我希望這個問題的標題是足夠清晰的> _ <

回答

2

首先值得注意的是你的traverse基本上是foldLeftMfoldLeftM適用於Foldable實例,該實例不包括Iterable,但我建議在與Scalaz一起工作時避免使用Iterable

所以你可以這樣寫:

a.foldLeftM[({ type L[x] = E \/ x })#L, B](b)(f) 

或者:

type MyErrorOr[A] = MyError \/ A 

a.foldLeftM[MyErrorOr, B](b)(f) 

Writer情況下,我建議不要建立在蓄能器Writer值。相反,你可以使用EitherT單子轉換:

type StringWriter[A] = Writer[String, A] 
type LoggedOr[E, A] = EitherT[StringWriter, E, A] 

現在你可以再次使用foldLeftM

def traverseLogged[A, B, E](a: List[A], b: B)(
    f: (B, A) => LoggedOr[E, B] 
): LoggedOr[E, B] = a.foldLeftM[({ type L[x] = LoggedOr[E, x] })#L, B](b)(f)(
    EitherT.eitherTMonad[StringWriter, E] 
) 

(說實話,我不知道爲什麼你必須明確地提供這裏 - 單子實例如果我們有我們的LoggedOr一個類型別名有固定E它會工作得很好。)

現在,當你運行的結果,你會得到一個Writer[String, E \/ B],這是相同的起源al traverseLogged。實際上,您可以在方法中執行此操作,並直接返回Logged,但如果您需要編寫像這樣的多個操作,則可能需要保持在LoggedOr的級別。

這假定你實際上並不需要訪問f中的累積Writer,但如果你這樣做了,那麼無論如何這意味着另一種類型可能更合適。

+0

非常感謝!我確信我的「遍歷」已經在scalaz中,但我沒有設法找到它。 對於traverseLogged,我需要在摺疊過程中對日誌進行線程化,因此我無法直接使用您的解決方案。然而,你給了我一些很好的咀嚼。我將在明天進一步研究。 (這裏有點晚) 再次,非常感謝! – lorilan

+0

好吧,我用一種新鮮的頭腦再次審視它。我設法實現了與您的「traverseLogged」幾乎相同的功能: http:// pastebin。然而,所有這些層次的變化似乎有點奇怪。其實我的B型是一​​個圖表,我做了一些轉換(可能會失敗)。但棘手的部分是,這些轉換中的一些可能是相互遞歸的,所以我需要改變級別。這些級別的變化是否正常,還是我需要更多地關注升級系列的功能(仍然是scalaz新手:p),還是我必須製作自定義類型,monad混合析取和作者? – lorilan