449 Stimmen

Scalaz iteratees: "Anheben" von `EnumeratorT` um `IterateeT` für ein "größeres" Monadenmatch anzupassen

Wenn ich einen EnumeratorT und einen entsprechenden IterateeT habe, kann ich sie gemeinsam 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 der Enumerator-Monad "größer" ist als der Iteratee-Monad, kann ich up oder allgemeiner Hoist verwenden, um den Iteratee anzugleichen:

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 der Iteratee-Monad "größer" ist als der Enumerator-Monad?

val en: EnumeratorT[String, Id] = ...
val it: IterateeT[String, Task, Int] = ...

it &= ???

Es scheint keinen Hoist-Instanz für EnumeratorT zu geben, noch eine offensichtliche "Anhebungs"-Methode.

7voto

Travis Brown Punkte 137441

Die übliche Kodierung eines Enumerators ist 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, ?]] umwandelt, gegeben ein F ~> G, werden Sie schnell auf ein Problem stoßen: Sie müssen einen Step[E, G, A] auf einen Step[E, F, A] "herabstufen", um den ursprünglichen Enumerator anwenden zu können.

Scalaz bietet auch eine alternative Enumerator-Kodierung an, 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 auf die Effekte eingeht, die er benötigt, aber der für Verbraucher, die reichere Kontexte benötigen, "angehoben" werden kann. Wir können Ihr Beispiel so ändern, dass EnumeratorP verwendet wird (und der neuere Natural-Transformation-Ansatz anstelle der alten Monadenteilreihenfolge):

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) }

Jetzt können wir die beiden wie folgt zusammensetzen:

scala> def result = (iter &= enum(toTask)).run
result: scalaz.concurrent.Task[Int]

EnumeratorP ist monadisch (wenn das F applikativ ist), und das EnumeratorP-Begleitobjekt bietet einige Funktionen zur Unterstützung bei der Definition von Enumeratoren, die denen auf EnumeratorT sehr ähnlich sehen — es gibt empty, perform, enumPStream, etc. Ich vermute, es muss EnumeratorT-Instanzen geben, die nicht mit der EnumeratorP-Kodierung implementiert werden konnten, aber mir fällt auf die Schnelle nicht ein, wie sie aussehen würden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X