Scalaz iteratees: “Lifting” `EnumeratorT` to match `IterateeT` for a “bigger” monad

In the usual encoding an enumerator is essentially a StepT[E, F, ?] ~> F[StepT[E, F, ?]]. If you try to write a generic method converting this type into a Step[E, G, ?] ~> G[Step[E, G, ?]] given an F ~> G, you’ll quickly run into an issue: you need to “lower” a Step[E, G, A] to a Step[E, F, A] in order to be able to apply the original enumerator.

Scalaz also provides an alternative enumerator encoding that looks like this:

trait EnumeratorP[E, F[_]] {
  def apply[G[_]: Monad](f: F ~> G): EnumeratorT[E, G]
}

This approach allows us to define an enumerator that’s specific about the effects it needs, but that can be “lifted” to work with consumers that require richer contexts. We can modify your example to use EnumeratorP (and the newer natural transformation approach rather than the old monad partial order):

import scalaz._, Scalaz._, iteratee._, concurrent.Task

def enum: EnumeratorP[String, Id] = ???
def iter: IterateeT[String, Task, Int] = ???

val toTask = new (Id ~> Task) { def apply[A](a: A): Task[A] = Task(a) }

We can now compose the two like this:

scala> def result = (iter &= enum(toTask)).run
result: scalaz.concurrent.Task[Int]

EnumeratorP is monadic (if the F is applicative), and the EnumeratorP companion object provides some functions to help with defining enumerators that look a lot like the ones on EnumeratorT—there’s empty, perform, enumPStream, etc. I guess there have to be EnumeratorT instances that couldn’t be implemented using the EnumeratorP encoding, but off the top of my head I’m not sure what they would look like.

Leave a Comment