Actually, we like Iteratee
because they compose. Therefore, instead of creating multiple Enumerator
from your original, you are more likely to compose two Iteratees sequentially (read-first and read-rest) and feed them using your only Enumerator.
For this you need a sequential composition method, now I call it andThen
. Here is an approximate implementation. Note that returning unused input is a little tough, maybe it can tweak the behavior with typeclass based on the input type. Also, it does not process the transfer of the remaining things from the first iterator to the second (Exercise :).
object Iteratees { def andThen[E, A, B](a: Iteratee[E, A], b: Iteratee[E, B]): Iteratee[E, (A,B)] = new Iteratee[E, (A,B)] { def fold[C]( done: ((A, B), Input[E]) => Promise[C], cont: ((Input[E]) => Iteratee[E, (A, B)]) => Promise[C], error: (String, Input[E]) => Promise[C]): Promise[C] = { a.fold( (ra, aleft) => b.fold( (rb, bleft) => done((ra, rb), aleft /* could be magicop(aleft, bleft)*/), (bcont) => cont(e => bcont(e) map (rb => (ra, rb))), (s, err) => error(s, err) ), (acont) => cont(e => andThen[E, A, B](acont(e), b)), (s, err) => error(s, err) ) } } }
Now you can simply use the following:
object Application extends Controller { def index = Action { Async { val strings: Enumerator[String] = Enumerator("1","2","3","4") val takeOne = Cont[String, String](e => e match { case Input.El(e) => Done(e, Input.Empty) case x => Error("not enough", x) }) val takeRest = Iteratee.consume[String]() val firstAndRest = Iteratees.andThen(takeOne, takeRest) val futureRes = strings(firstAndRest) flatMap (_.run) futureRes.map(x => Ok(x.toString)) // prints (1,234) } } }