我有一个ADT,它基本上是Option
和Try
之间的交叉:
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)
,请将a
提供给inner
Empty
,则为no-op 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)
所以我要找的是???
来定义harderEnumeratee
,以减轻已经实施easyClientEnumeratee
的用户的负担。
我想???
应该是Enumeratee[Result[AorB], AorB]
,但如果我尝试类似
Enumeratee.collect[Result[AorB]] {
case Success(ab) => ab
case Error(err) => throw err
}
实际上会抛出错误;我实际上希望错误以Error(err)
的形式返回。
答案 0 :(得分: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)