Si j'ai un EnumeratorT
et un IterateeT
correspondant, je peux les exécuter ensemble:
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 monade de l'énumérateur est "plus grande" que la monade des itérérés, je peux utiliser up
ou, plus généralement, Hoist
pour "soulever" l'itérée à faire correspondre:
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]
Mais que dois-je faire lorsque la monade itérative est "plus grosse" que la monade énumératrice?
val en: EnumeratorT[String, Id] = ...
val it: IterateeT[String, Task, Int] = ...
it &= ???
Il ne semble pas y avoir d'instance Hoist
pour EnumeratorT
, ni de méthode "lift" évidente.
Dans le codage habituel, un énumérateur est essentiellement un StepT[E, F, ?] ~> F[StepT[E, F, ?]]
. Si vous essayez d'écrire une méthode générique convertissant ce type en Step[E, G, ?] ~> G[Step[E, G, ?]]
donné un F ~> G
, vous rencontrerez rapidement un problème: vous devez "baisser" un Step[E, G, A]
à un Step[E, F, A]
afin de pouvoir appliquer l'énumérateur d'origine.
Scalaz fournit également n encodage d'énumérateur alternatif qui ressemble à ceci:
trait EnumeratorP[E, F[_]] {
def apply[G[_]: Monad](f: F ~> G): EnumeratorT[E, G]
}
Cette approche nous permet de définir un énumérateur qui est spécifique sur les effets dont il a besoin, mais qui peut être "levé" pour travailler avec des consommateurs qui nécessitent des contextes plus riches. Nous pouvons modifier votre exemple pour utiliser EnumeratorP
(et la nouvelle approche de transformation naturelle plutôt que l'ancien ordre partiel monade):
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) }
Nous pouvons maintenant composer les deux comme ceci:
scala> def result = (iter &= enum(toTask)).run
result: scalaz.concurrent.Task[Int]
EnumeratorP
est monadique (si le F
est applicatif), et l'objet compagnon EnumeratorP
fournit quelques fonctions pour aider à définir des énumérateurs qui ressemblent beaucoup à ceux sur EnumeratorT
— il y a empty
, perform
, enumPStream
, etc. Je suppose qu'il doit y avoir EnumeratorT
instances qui ne pourraient pas être implémentées en utilisant le EnumeratorP
encodage, mais du haut de ma tête, je ne sais pas à quoi ils ressembleraient.