Scalaz iteratees: 'Elevating' 'EnumeratorT` para que coincida con' IterateeT` para una mónada “más grande”

445

Si tengo un EnumeratorTy un correspondiente IterateeT, 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 upo, más generalmente, Hoistpara "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 Hoistinstancia EnumeratorTni un método obvio de "elevación".

lmm
fuente
59
+1 para una pregunta ordenada, pero desde el principio no estoy seguro de mi cabeza de que esto sea posible en el caso general, ya que en Enumeratorrealidad es solo una envoltura alrededor de a StepT => IterateeT, lo que sugiere que tendrá que "renunciar" de a StepT[E, BigMonad, A].
Travis Brown
12
Sí, lo encontré cuando intenté implementarlo directamente. Pero lógicamente, an Enumeratores solo una fuente efectiva, ¿verdad? Parece que debería poder usar algo que puede suministrar Apara suministrar Task[A].
lmm
8
No sé lo suficiente sobre Scala como para ofrecer una respuesta, pero ¿no podría definir su propio tipo y proporcionarle un mecanismo de elevación ?
Rob
8
No, eso no es lo mismo, es un tipo diferente de "levantamiento".
lmm
2
@TravisBrown hay una recompensa por esto en este momento, si quieres escribirlo.
Aaron Hall

Respuestas:

4

En la codificación habitual, un enumerador es esencialmente a 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 F ~> G, rápidamente se encontrará con un problema: debe "bajar" a Step[E, G, A]a 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]

EnumeratorPes monádica (si el Fes aplicativo), y el EnumeratorPobjeto acompañante proporciona algunas funciones para ayudar con la definición de los encuestadores que se parecen mucho a los que están en EnumeratorT-Hay empty, perform, enumPStream, etc supongo que tiene que haber EnumeratorTinstancias que no se podrían aplicar aprovechando la EnumeratorPcodificación, pero fuera de mi cabeza no estoy seguro de cómo se verían.

Travis Brown
fuente