Wenn ich ein EnumeratorT
und ein entsprechendes IterateeT
habe, kann ich sie zusammen ausführen:
val en: EnumeratorT[String, Task] = EnumeratorT.enumList(List("a", "b", "c"))
val it: IterateeT[String, Task, Int] = IterateeT.length
(it &= en).run : Task[Int]
Wenn die Enumerator-Monade "größer" als die Iteratee-Monade ist, kann ich up
oder allgemeiner Hoist
verwenden, um den Iteratee entsprechend zu "heben":
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]
Aber was mache ich, wenn die iterierte Monade "größer" ist als die Enumerator-Monade?
val en: EnumeratorT[String, Id] = ...
val it: IterateeT[String, Task, Int] = ...
it &= ???
Es scheint weder eine Hoist
-Instanz für EnumeratorT
noch eine offensichtliche "Lift" -Methode zu geben.
In der üblichen Codierung ist ein Enumerator im Wesentlichen ein StepT[E, F, ?] ~> F[StepT[E, F, ?]]
. Wenn Sie versuchen, eine generische Methode zu schreiben, die diesen Typ in einen Step[E, G, ?] ~> G[Step[E, G, ?]]
mit einem F ~> G
konvertiert, tritt schnell ein Problem auf: Sie müssen einen Step[E, G, A]
auf einen Step[E, F, A]
"senken", um den ursprünglichen Enumerator anwenden zu können.
Scalaz bietet auch eine alternative Enumerator-Codierung , die so aussieht:
trait EnumeratorP[E, F[_]] {
def apply[G[_]: Monad](f: F ~> G): EnumeratorT[E, G]
}
Dieser Ansatz ermöglicht es uns, einen Enumerator zu definieren, der spezifisch für die benötigten Effekte ist, der jedoch "aufgehoben" werden kann, um mit Verbrauchern zu arbeiten, die umfangreichere Kontexte benötigen. Wir können Ihr Beispiel so ändern, dass es EnumeratorP
verwendet (und den neueren Ansatz der natürlichen Transformation anstelle der alten Teilreihenfolge der Monaden):
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) }
Wir können die beiden nun so zusammensetzen:
scala> def result = (iter &= enum(toTask)).run
result: scalaz.concurrent.Task[Int]
EnumeratorP
ist monadisch (wenn F
anwendbar ist), und das Begleitobjekt EnumeratorP
bietet einige Funktionen zur Definition von Enumeratoren, die denen in EnumeratorT
- Es gibt empty
, perform
, enumPStream
usw. Ich denke, es muss EnumeratorT
Instanzen geben, die mit EnumeratorP
Codierung, aber auf den ersten Blick bin ich mir nicht sicher, wie sie aussehen würden.