Created
May 23, 2013 21:40
-
-
Save eamelink/5639642 to your computer and use it in GitHub Desktop.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
await can't print value on worksheet.
any idea?