If I have an EnumeratorT and a corresponding IterateeT I can run them together:
val en: EnumeratorT[String, Task] = EnumeratorT.enumList(List("a", "b", "c"))
val it: IterateeT[String, Task, Int] = IterateeT.length
(it &= en).run : Task[Int]
If the enumerator monad is "bigger" than the iteratee monad, I can use up or, more generally, Hoist to "lift" the iteratee to match:
val en: EnumeratorT[String, Task] = ...
val it: IterateeT[String, Id, Int] = ...
val liftedIt = IterateeT.IterateeTMonadTrans[String].hoist(
implicitly[Task |>=| Id]).apply(it)
(liftedIt &= en).run: Task[Int]
But what do I do when the iteratee monad is "bigger" than the enumerator monad?
val en: EnumeratorT[String, Id] = ...
val it: IterateeT[String, Task, Int] = ...
it &= ???
There doesn't seem to be a Hoist instance for EnumeratorT, nor any obvious "lift" method.
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 aStep[E, G, ?] ~> G[Step[E, G, ?]]given anF ~> G, you'll quickly run into an issue: you need to "lower" aStep[E, G, A]to aStep[E, F, A]in order to be able to apply the original enumerator.Scalaz also provides an alternative enumerator encoding that looks like this:
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):We can now compose the two like this:
EnumeratorPis monadic (if theFis applicative), and theEnumeratorPcompanion object provides some functions to help with defining enumerators that look a lot like the ones onEnumeratorT—there'sempty,perform,enumPStream, etc. I guess there have to beEnumeratorTinstances that couldn't be implemented using theEnumeratorPencoding, but off the top of my head I'm not sure what they would look like.