How to create an Iteratee that passes through values to an inner Iteratee unless a specific value is found

I've got an ADT that's essentially a cross between Option and 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]

(assume common combinators like map, flatMap etc are defined on Result)

Given an Iteratee[A, Result[B] called inner, I want to create a new Iteratee[Result[A], Result[B]] with the following behavior:

  • If the input is a Success(a), feed a to inner
  • If the input is an Empty, no-op
  • If the input is an Error(err), I want inner to be completely ignored, instead returning a Done iteratee with the Error(err) as its result.

Example Behavior:

// 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"))

This sounds to me like the job for an Enumeratee, but I haven't been able to find anything in the docs that looks like it'll do what I want, and the internal implementations are still voodoo to me.

How can I implement wrapForResultInput to create the behavior described above?


Adding some more detail that won't really fit in a comment:

Yes it looks like I was mistaken in my question. I described it in terms of Iteratees but it seems I really am looking for Enumeratees.

At a certain point in the API I'm building, there's a Transformer[A] class that is essentially an Enumeratee[Event, Result[A]]. I'd like to allow clients to transform that object by providing an Enumeratee[Result[A], Result[B]], which would result in a Transformer[B] aka an Enumeratee[Event, Result[B]].

For a more complex example, suppose I have a Transformer[AorB] and want to turn that into a 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)

The client could implement an Enumeratee[AorB, Result[(A, List[B])]] without too much trouble using Enumeratee.grouped, but they are required to provide an Enumeratee[Result[AorB], Result[(A, List[B])] which seems to introduce a lot of complication that I'd like to hide from them if possible.

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)

So what I'm looking for is the ??? to define the harderEnumeratee in order to ease the burden on the user who already implemented easyClientEnumeratee.

I guess the ??? should be an Enumeratee[Result[AorB], AorB], but if I try something like

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

the error will actually be thrown; I actually want the error to come back out as an Error(err).

Answers


Simplest implementation of such would be Iteratee.fold2 method, that could collect elements until something is happened.

Since you return single result and can't really return anything until you verify there is no errors, Iteratee would be enough for such a task

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

Now if we'll prepare little playground

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)

we can verify those results

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

Need Your Help

Uncaught Error: Module did not self-register

javascript node.js node-webkit node-ffi

I try to use node-vlc with nw.js (v0.12.0-alpha2). When i launch my app without nw.js it works, but when i launch it with nw.js i got an error: