Skip to content

Instantly share code, notes, and snippets.

@nafg
Created July 31, 2013 10:09
Show Gist options
  • Save nafg/6120889 to your computer and use it in GitHub Desktop.
Save nafg/6120889 to your computer and use it in GitHub Desktop.
two way iteratee based on jsuereth and others
package chavrusa.ivr
import Trampoline.{ Done => TDone, More => TMore, Cont => TCont }
trait Iteratees[Err] {
sealed trait Input[+I]
case class El[+I](value: I) extends Input[I]
case object Empty extends Input[Nothing]
case object EOF extends Input[Nothing]
sealed trait Step[I, A]
case class Cont[I, A](data: Option[Err], k: Input[I] => Iteratee[I, A]) extends Step[I, A]
case class Done[I, A](result: A, remaining: Input[I]) extends Step[I, A]
case class Error[I, A](err: Err, resume: Input[I]) extends Step[I, A]
type Identity[A] = A
trait Iteratee[I, O] {
def fold[A, F[_]](f: Step[I, O] => F[A]): F[A]
def flatMap[P](next: O => Iteratee[I, P]): Iteratee[I, P] = this.fold[Iteratee[I, P], Trampoline]{
case Done(a, remaining) =>
TMore{() =>
println(s"got Done($a, $remaining)")
TDone(next(a).fold[Iteratee[I, P], Identity] {
case Cont(_, k) =>
println("next(a) was a Cont(k), remaining = " + remaining)
k(remaining)
case other => Iteratee(other)
})}
case Error(e, r) => TDone(Iteratee(Error[I, P](e, r)))
case Cont(d, k) => TMore{ () =>
TDone(Iteratee(Cont(d, (in: Input[I]) => k(in) flatMap next)))
}
}.run
def zip[P](that: Iteratee[I, P]) = new ZippedIteratee(this, that)
def run: Either[Err, O] = {
def loop(it: Iteratee[I, O]): Trampoline[Either[Err, O]] = it fold {
case Cont(d, k) => loop(k(EOF))
case Done(a, _) => TDone(Right(a))
case Error(err, _) => TDone(Left(err))
}
loop(this).run
}
}
case class SimpleIteratee[I, O](step: Step[I, O]) extends Iteratee[I, O] {
def fold[A, F[_]](f: Step[I, O] => F[A]) = f(step)
}
object Iteratee {
def apply[I, O](s: Step[I, O]) = SimpleIteratee(s)
}
class ZippedIteratee[I, A, B](a: Iteratee[I, A], b: Iteratee[I, B]) extends Iteratee[I, (A, B)] {
private def drive(i: Input[I]): Iteratee[I, (A, B)] =
new ZippedIteratee(Enumerator.once(i) apply a, Enumerator.once(i) apply b)
def fold[R, F[_]](f: Step[I, (A, B)] => F[R]): F[R] = a fold { sa =>
b fold { sb =>
(sa, sb) match {
case (Done(a, i), Done(b, _)) => f(Done((a, b), i))
case (Error(e, res), _) => f(Error(e, res))
case (_, Error(e, res)) => f(Error(e, res))
case (Cont(msg, _), _) => f(Cont(msg, drive))
case (_, Cont(msg, _)) => f(Cont(msg, drive))
case _ => f(Cont(None, drive))
}
}
}
}
trait Enumerator[I] { outer =>
def apply[O](input: Iteratee[I, O]): Iteratee[I, O]
// def convert[J](f: Enumeratee[I, J]): Enumerator[J] = new Enumerator[J] {
// def apply[O](c: Iteratee[J, O]): Iteratee[J, O] = {
// def step(x: Iteratee[I, Iteratee[J, O]]): Trampoline[Iteratee[J, O]] = x fold {
// case Done(a, i) => TDone(a)
// case Error(e, res) => TDone(Iteratee(Error(e, EOF))) // FIXME resume=?
// case Cont(d, k) => TMore{ () =>
// step(k(Empty)) //FIXME this can't be right...
// }
// }
// step(outer apply (f apply c)).run
// }
// }
}
object Enumerator {
def once[I](value: Input[I]): Enumerator[I] = new Enumerator[I] {
def apply[O](input: Iteratee[I, O]) = (input.fold[Iteratee[I, O], Identity] {
case Cont(_, k) => k(value)
case other => Iteratee(other)
})
}
/*def list[E](xs: List[E]): Enumerator[E] = {
def loop[A](xs: List[E], iteratee: Iteratee[E, A]): Trampoline[Iteratee[E, A]] = xs match {
case Nil => TDone(iteratee)
case x :: xs => iteratee fold {
case Cont(k) => TMore(() => loop(xs, k(El(x))))
case _ => TDone(iteratee)
}
}
new Enumerator[E] {
def apply[A](iteratee: Iteratee[E, A]) = loop(xs, iteratee).run
}
}*/
}
trait Enumeratee[I, J] {
def apply[O](i: Iteratee[J, O]): Iteratee[I, Iteratee[J, O]]
def convert[O](i: Iteratee[J, O]): Iteratee[I, O] = ???
}
def counter[I]: Iteratee[I, Long] = {
def step(n: Long): Input[I] => Iteratee[I, Long] = {
case El(x) => Iteratee(Cont(None, step(n + 1)))
case Empty => Iteratee(Cont(None, step(n)))
case EOF => Iteratee(Done(n, EOF))
}
Iteratee(Cont(None, step(0)))
}
def consume(data: Seq[Int] = Nil): Iteratee[Int, Seq[Int]] = Iteratee(Cont(None, {
case El(byte) => consume(data :+ byte)
case Empty => consume(data)
case EOF => Iteratee(Done(data, EOF))
}))
}
object Iteratees extends Iteratees[String] {
def peek[I]: Iteratee[I, Option[I]] = Iteratee(Cont(None, {
case El(i) => Iteratee(Done(Some(i), El(i)))
case EOF => Iteratee(Done(None, Empty))
case Empty => peek
}))
def head[I]: Iteratee[I, Option[I]] = Iteratee(Cont(None, {
case El(i) => Iteratee(Done(Some(i), Empty))
case EOF => Iteratee(Done(None, Empty))
case Empty => head
}))
}
object TestIteratees extends Iteratees[String] with App {
trait Enumerator[I] extends super.Enumerator[I] {
}
def consume2(data: Seq[Int] = Nil): Iteratee[Int, Seq[Int]] = Iteratee(Cont(None, {
case El(byte) => consume(data :+ byte)
case Empty => consume(data)
case EOF => Iteratee(Done(data, EOF))
}))
def echoIteratee: Iteratee[String, String] = {
def handler: Input[String] => Iteratee[String, String] = {
case El("quit") => Iteratee(Done("Goodbye", EOF))
case El(s) => Iteratee(Cont(Some(s"You said: '$s'"), handler))
case Empty => echoIteratee
case EOF => Iteratee(Error("The end", EOF))
}
Iteratee(Cont[String, String](Some("Please enter some text"), handler))
}
// def repeat: Iteratee[String, String] = echoIteratee flatMap { _ => repeat }
def enumerateIt(it: Iteratee[String, String]): IO[Iteratee[String, String]] = {
def loop: Iteratee[String, String] => IO[Iteratee[String, String]] = _ fold {
case d @ Done(s, next) => for {
_ <- IO { println(s) }
i <- IO { it }
} yield i
case Cont(d, k) =>
IO{ d foreach println } flatMap { _ =>
IO{ Console.readLine } flatMap { s =>
if(s == null) IO { Iteratee(Done("", EOF)) }
else loop(k(El(s)))
}
}
case Error(msg, resume) =>
println("Got error: "+msg )
IO { Iteratee(Done(msg, resume)) }
}
loop(it)
}
// def repeat: Trampoline[Iteratee[String, String]] = TDone(echoIteratee) flatMap (_ => repeat)
enumerateIt(echoIteratee).unsafePerformIO.run
// def inputs[E]: Enumerator[E] = {
// def loop[A](xs: List[E], iteratee: Iteratee[E, A]): Trampoline[Iteratee[E, A]] = xs match {
// case Nil => TDone(iteratee)
// case x :: xs => iteratee fold {
// case Cont(k) => TMore(() => loop(xs, k(El(x))))
// case _ => TDone(iteratee)
// }
// }
// new Enumerator[E] {
// def apply[A](iteratee: Iteratee[E, A]) = loop(xs, iteratee).run
// }
// }
}
object IvrIteratees extends Iteratees[Sayable] {
/*def sillyIteratee: Iteratee[Char, Sayable] = Iteratee(Cont{
case El(c) => Done(ChavrusaSayables.digitWords(c.toString)(null), EOF)
case Empty => sillyIteratee
case EOF =>
})*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment