public
Created

Scala-IDE worksheet with some examples of iteratees, enumerators and enumeratees and how to use and compose them. Originated from a presentation at Dutch Scala Enthusiasts.

  • Download Gist
iteratees-by-example.scala
Scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
import play.api.libs.iteratee._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import scala.concurrent.duration._
import play.api.libs.concurrent.Promise
 
object iteratees {
 
// Implicit conversion to add 'await' to a Future
implicit class WFuture[A](val inner: Future[A]) extends AnyVal {
def await(implicit timeout: Duration) = Await.result(inner, timeout)
}
 
// Default timeout for futures.
implicit val timeout = 10 second //> timeout : scala.concurrent.duration.FiniteDuration = 10 seconds
 
// Simple enumerator with pre-known values
val enumerator = Enumerator(1, 2, 3, 4, 5) //> enumerator : play.api.libs.iteratee.Enumerator[Int] = play.api.libs.iterate
//| e.Enumerator$$anon$21@571a75a2
// Iteratee that just prints all input
val printIteratee = Iteratee.foreach[Int] { i => print("[" + i + "]") }
//> printIteratee : play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.libs.i
//| teratee.Cont$$anon$3@6f25844f
// Apply anumerator to iteratee
(enumerator |>> printIteratee).await //> [1][2][3][4][5]res0: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.li
//| bs.iteratee.Iteratee$$anon$1@148238f4
 
// Create an iteratee that just takes the first five elements, by transforming the regular
// printIteratee with an enumeratee that only takes five elements.
def printFiveIteratee = Enumeratee.take(5) &>> printIteratee
//> printFiveIteratee: => play.api.libs.iteratee.Iteratee[Int,Unit]
 
// Test whether our printFiveIteratee actually only consumes the
// first five elements of an enumerator with more chunks
(Enumerator(1, 2, 3, 4, 5, 6, 7, 8) |>> printFiveIteratee).await
//> [1][2][3][4][5]res1: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l
//| ibs.iteratee.Iteratee$$anon$1@716c9867
 
// Now we have the printFiveIteratee, we can make endless enumerators and still show
// their first chunks, without blowing up.
val ones = Enumerator.repeat(1) //> ones : play.api.libs.iteratee.Enumerator[Int] = play.api.libs.iteratee.Enu
//| merator$$anon$15@5982bcde
 
// Print the first five chunks from an infinite enumerator
(ones |>> printFiveIteratee).await //> [1][1][1][1][1]res2: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l
//| ibs.iteratee.Done$$anon$2@4e513d61
 
// So far, our enumerators had no internal state.
// Here we create one with the 'unfold' method that has
// a state, and uses the state to compute an element and a new state
val counting = Enumerator.unfold(0)(counter => Some(counter + 1, counter))
//> counting : play.api.libs.iteratee.Enumerator[Int] = play.api.libs.iteratee
//| .Enumerator$$anon$12@2bb5340c
// Test it
(counting |>> printFiveIteratee).await //> [0][1][2][3][4]res3: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l
//| ibs.iteratee.Done$$anon$2@71060478
 
// Create an enumerator with state, that computes the element
// and new state asynchronously. The 'Promise' is a play.api.libs.concurrent.Promise,
// not the scala.concurrent one, because the Play one has a nice 'timeout' method
// to create a future that will be completed after some time.
val slowCounting = Enumerator.unfoldM(0) { counter =>
Promise.timeout(Some(counter + 1, counter), 1 second)
} //> slowCounting : play.api.libs.iteratee.Enumerator[Int] = play.api.libs.iter
//| atee.Enumerator$$anon$12@60491c4c
// Apply our slow enumerator on the printing iteratee
// Note that this prints one element every second.
// Commented out by default, because it's slow
// (slowCounting |>> printFiveIteratee).await
 
// So far, our iteratees were stateless. Here we create
// an iteratee that maintains state, using the 'fold' method on the
// Iteratee object. Notice the similarity with the 'fold' method
// on classes from the collections api.
val summingIteratee = Iteratee.fold(0)((state, elem: Int) => state + elem)
//> summingIteratee : play.api.libs.iteratee.Iteratee[Int,Int] = play.api.libs
//| .iteratee.Cont$$anon$3@7632efa7
// With an iteratee that has state, it's actually interesting
// to look at the iteratee future that the enumerator produces
// after being applied to the original iteratee.
// The enumerator returns the iteratee as it is after pushing
// in all the chunks. So this iteratee will have an internal state
// of '15'
val iterateeFuture = (enumerator |>> summingIteratee)
//> iterateeFuture : scala.concurrent.Future[play.api.libs.iteratee.Iteratee[I
//| nt,Int]] = scala.concurrent.impl.Promise$DefaultPromise@a13f991
// With the 'run' method on an Iteratee, we feed it an EOF
// and then extract the value.
Await.result(iterateeFuture.flatMap(_.run), 1 second)
//> res4: Int = 15
 
// An enumeratee that only keeps even numbers
val evenEnumeratee = Enumeratee.filter[Int](i => i % 2 == 0)
//> evenEnumeratee : play.api.libs.iteratee.Enumeratee[Int,Int] = play.api.lib
//| s.iteratee.Enumeratee$$anon$18@6f878144
 
// This transforms (&>>) the iteratee with the enumeratee
(counting |>> evenEnumeratee &>> printFiveIteratee).await
//> [0][2][4][6][8]res5: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l
//| ibs.iteratee.Done$$anon$2@15f48262
// Same as the above, but with parentheses to make the operator
// precedence explicit
(counting |>> (evenEnumeratee &>> printFiveIteratee)).await
//> [0][2][4][6][8]res6: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l
//| ibs.iteratee.Done$$anon$2@679bfb30
 
// This "throughs" the enumerator through the enumerate. Give the
// same result as the previous two, but this time the enumeratee is
// composed with the enumerator, and not with the iteratee.
// So whether you are dealing with an Enumerator or an Iteratee,
// you can always compose with an Enumeratee.
(counting &> evenEnumeratee |>> printFiveIteratee).await
//> [0][2][4][6][8]res7: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l
//| ibs.iteratee.Iteratee$$anon$1@7d95d4fe
// Same as the above
((counting &> evenEnumeratee) |>> printFiveIteratee).await
//> [0][2][4][6][8]res8: play.api.libs.iteratee.Iteratee[Int,Unit] = play.api.l
//| ibs.iteratee.Iteratee$$anon$1@77d2b01b
 
}

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.