Scalaz iteratees: «Lifting» «EnumeratorT» pour correspondre à «IterateeT» pour une monade «plus grosse»

445

Si j'ai un EnumeratorTet un correspondant, IterateeTje 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 énumératrice est "plus grosse" que la monade itérée, je peux utiliser upou, plus généralement, Hoist"lever" l'itérée pour qu'elle corresponde:

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' Hoistexemple EnumeratorTni de méthode de "lift" évidente.

lmm
la source
59
+1 pour une question soignée, mais au départ, je ne suis pas sûr de ma tête que cela est possible dans le cas général, car un Enumeratorest vraiment juste un wrapper autour d'un StepT => IterateeT, ce qui suggère que vous devrez "démissionner" d'un StepT[E, BigMonad, A].
Travis Brown
12
Oui, j'ai trouvé ça quand j'ai essayé de l'implémenter directement. Mais logiquement, Enumeratorc'est juste une source efficace, non? J'ai l'impression que je devrais pouvoir utiliser une chose qui peut fournir Aà fournir Task[A].
lmm
8
Je ne connais pas assez Scala pour offrir une réponse, mais ne pourriez-vous pas définir votre propre type et fournir un mécanisme de levage pour cela ?
Rob
8
Non, ce n'est pas du tout la même chose, c'est un autre type de "lifting".
lmm
2
@TravisBrown il y a une prime sur celui-ci en ce moment, si vous voulez l'écrire.
Aaron Hall

Réponses:

4

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 un Step[E, G, ?] ~> G[Step[E, G, ?]]donné F ~> G, vous rencontrerez rapidement un problème: vous devez "abaisser" un Step[E, G, A]à un Step[E, F, A]afin de pouvoir appliquer l'énumérateur d'origine.

Scalaz fournit également un 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 à utiliser EnumeratorP(et la nouvelle approche de transformation naturelle plutôt que l'ancien ordre partiel de la 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]

EnumeratorPest monadique (si le Fest applicatif), et l' EnumeratorPobjet compagnon fournit des fonctions pour aider à définir les agents recenseurs qui ressemblent beaucoup à ceux sur EnumeratorTde -Il empty, perform, enumPStream, etc. Je pense qu'il faut être des EnumeratorTcas qui ne pouvaient pas être mises en œuvre à l' aide l' EnumeratorPencodage, mais du haut de ma tête, je ne sais pas à quoi ils ressembleraient.

Travis Brown
la source