2015-11-25 18 views
2

我有一個ADT這是本質OptionTry之間的交叉如何創建通過值內Iteratee傳遞一個Iteratee:除非特定值時發現

sealed trait Result[+T] 
case object Empty extends Result[Nothing] 
case class Error(cause: Throwable) extends Result[Nothing] 
case class Success[T](value: T) extends Result[T] 

(假設像map共同組合子,flatMap等在結果上定義)

給定一個Iteratee[A, Result[B]稱爲inner,我想創建一個新的Iteratee[Result[A], Result[B]]有以下行爲:

  • 如果輸入的是Success(a),飼料ainner
  • 如果輸入的是一個Empty,無操作
  • 如果輸入的是一個Error(err),我想inner被完全忽略,而不是返回Done作爲其結果迭代Error(err)

實例行爲:

// inner: Iteratee[Int, Result[List[Int]]] 
// inputs: 
1 
2 
3 
// output: 
Success(List(1,2,3)) 

// wrapForResultInput(inner): Iteratee[Result[Int], Result[List[Int]]] 
// inputs: 
Success(1) 
Success(2) 
Error(Exception("uh oh")) 
Success(3) 

// output: 
Error(Exception("uh oh")) 

這聲音,我喜歡這份工作爲Enumeratee,但我一直沒能在the docs看起來像它會做我想做什麼,發現了什麼,內部的實現對我來說依然是巫術。

如何實現wrapForResultInput來創建上述行爲?


添加一些不會真正適合評論更多詳細信息:

是的,它看起來像我是在我的問題是錯誤的。我用Iteratees來描述它,但似乎我真的在尋找Enumeratees

在我正在建造的API中的某個點上,有一個Transformer[A]類,實質上是Enumeratee[Event, Result[A]]。我希望允許客戶通過提供Enumeratee[Result[A], Result[B]]來轉換該對象,這將導致Transformer[B]又名Enumeratee[Event, Result[B]]

對於更復雜的例子,假設我有一個Transformer[AorB],並希望把它轉換成一個Transformer[(A, List[B])]

// the Transformer[AorB] would give 
a, b, a, b, b, b, a, a, b 

// but the client wants to have 
a -> List(b), 
a -> List(b, b, b), 
a -> Nil 
a -> List(b) 

客戶端可以實現一個Enumeratee[AorB, Result[(A, List[B])]]不使用Enumeratee.grouped太麻煩,但他們需要提供一個Enumeratee[Result[AorB], Result[(A, List[B])]這似乎介紹了很多併發症,我想盡可能隱藏起來。

val easyClientEnumeratee = Enumeratee.grouped[AorB]{ 
    for { 
    _ <- Enumeratee.dropWhile(_ != a) ><> Iteratee.ignore 
    headResult <- Iteratee.head.map{ Result.fromOption } 
    bs <- Enumeratee.takeWhile(_ == b) ><> Iteratee.getChunks 
} yield headResult.map{_ -> bs} 

val harderEnumeratee = ??? ><> easyClientEnumeratee 

val oldTransformer: Transformer[AorB] = ... // assume it already exists 
val newTransformer: Transformer[(A, List[B])] = oldTransformer.andThen(harderEnumeratee) 

所以,我正在尋找的是???,以緩解對誰已經實施easyClientEnumeratee用戶的負擔定義harderEnumeratee

我猜???應該是一個Enumeratee[Result[AorB], AorB],但如果我嘗試像

Enumeratee.collect[Result[AorB]] { 
    case Success(ab) => ab 
    case Error(err) => throw err 
} 

錯誤實際上將拋出;我實際上想讓錯誤以Error(err)的形式返回。

+0

哦,我剛剛意識到。只要發生錯誤,就要完成Iteratee .... – Odomontois

+0

這是正確的。 – Dylan

回答

1

最簡單的實現方法是Iteratee.fold2方法,它可以收集元素,直到發生某些事情。

既然你返回一個結果,並不能真正返回任何東西,直到你確認沒有錯誤,Iteratee會爲這樣一個任務是夠了

def listResults[E] = Iteratee.fold2[Result[E], Either[Throwable, List[E]]](Right(Nil)) { (state, elem) => 
    val Right(list) = state 
    val next = elem match { 
    case Empty => (Right(list), false) 
    case Success(x) => (Right(x :: list), false) 
    case Error(t) => (Left(t), true) 
    } 
    Future(next) 
} map { 
    case Right(list) => Success(list.reverse) 
    case Left(th) => Error(th) 
} 

如果我們把準備好的小操場

import scala.concurrent.ExecutionContext.Implicits._ 
import scala.concurrent.{Await, Future} 
import scala.concurrent.duration._ 

val good = Enumerator.enumerate[Result[Int]](
    Seq(Success(1), Empty, Success(2), Success(3))) 

val bad = Enumerator.enumerate[Result[Int]](
    Seq(Success(1), Success(2), Error(new Exception("uh oh")), Success(3))) 

def runRes[X](e: Enumerator[Result[X]]) : Result[List[X]] = Await.result(e.run(listResults), 3 seconds) 

我們可以驗證這些結果

runRes(good) //res0: Result[List[Int]] = Success(List(1, 2, 3)) 
runRes(bad) //res1: Result[List[Int]] = Error(java.lang.Exception: uh oh) 
+0

感謝您的回覆。這很有幫助,雖然不是我所要求的。我正在創建一個函數/枚舉器/可以應用於* any *'inner:Iteratee [A,Result [B]]的任何內容。預期用法是在客戶端實現'inner'的庫中,我希望通過提供這個函數來隱藏處理成功/錯誤所增加的複雜性。我提供的簡單例子只是一個這樣的「內在」的例子。我一定會在我的文檔中提到'fold2'方法。這就是說,泛化函數/枚舉是可行的嗎? – Dylan

+0

@Dylan'Iteratee [A,Result [B]]'是來自'A'元素流的單個Result [B]的生成器。你確定,它是有意設計的? – Odomontois

+0

我想說的話不適合發表評論,所以我在問題中增加了更多細節。看到我上面的編輯。我想我要找的實際上是一個'Enumeratee [Result [A],A]',但是一個簡單的'Collect'不會因爲我想要的行爲而削減它。 – Dylan