Scalaz iteratees: ''Elevating'' ''EnumeratorT` para que coincida con'' IterateeT` para una mónada “más grande”
monads (1)
En la codificación habitual, un enumerador es esencialmente un
StepT[E, F, ?] ~> F[StepT[E, F, ?]]
.
Si intenta escribir un método genérico que convierta este tipo en un
Step[E, G, ?] ~> G[Step[E, G, ?]]
Dado un
F ~> G
, rápidamente se encontrará con un problema: necesita "bajar" un
Step[E, G, A]
a un
Step[E, F, A]
para poder aplicar el enumerador original.
Scalaz también proporciona una codificación de enumerador alternativa que se ve así:
trait EnumeratorP[E, F[_]] {
def apply[G[_]: Monad](f: F ~> G): EnumeratorT[E, G]
}
Este enfoque nos permite definir un enumerador que sea específico sobre los efectos que necesita, pero que puede "levantarse" para trabajar con consumidores que requieren contextos más ricos.
Podemos modificar su ejemplo para usar
EnumeratorP
(y el nuevo enfoque de transformación natural en lugar del antiguo orden parcial de mónada):
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) }
Ahora podemos componer los dos así:
scala> def result = (iter &= enum(toTask)).run
result: scalaz.concurrent.Task[Int]
EnumeratorP
es monádico (si la
F
es aplicativa), y el objeto complementario
EnumeratorP
proporciona algunas funciones para ayudar a definir enumeradores que se parecen mucho a los de
EnumeratorT
: hay
empty
,
perform
,
enumPStream
, etc. Supongo que tiene que haber
EnumeratorT
instancias que no se pudieron implementar usando la codificación
EnumeratorP
, pero fuera de mi cabeza no estoy seguro de cómo se verían.
Si tengo un
EnumeratorT
y un
IterateeT
correspondiente, puedo ejecutarlos juntos:
val en: EnumeratorT[String, Task] = EnumeratorT.enumList(List("a", "b", "c"))
val it: IterateeT[String, Task, Int] = IterateeT.length
(it &= en).run : Task[Int]
Si la mónada enumeradora es "más grande" que la mónada iterada, puedo usar o, más en general,
Hoist
para "levantar" la iteración para que coincida:
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]
Pero, ¿qué hago cuando la mónada iteratee es "más grande" que la mónada enumeradora?
val en: EnumeratorT[String, Id] = ...
val it: IterateeT[String, Task, Int] = ...
it &= ???
No parece haber una instancia de
Hoist
para
EnumeratorT
, ni ningún método obvio de "elevación".