Iterados Scalaz: “Levantar” o `EnumeratorT` para corresponder ao` IterateeT` para uma mônada “maior”

445

Se eu tiver um EnumeratorTe um correspondente IterateeT, posso executá-los 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]

Se a mônada do enumerador for "maior" que a mônada do iterado, eu posso usar upou, de maneira mais geral, Hoist"elevar" o iterado para corresponder:

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]

Mas o que faço quando a mônada iterada é "maior" que a mônada do enumerador?

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

it &= ???

Parece não haver um Hoistexemplo EnumeratorTnem método óbvio de "levantamento".

lmm
fonte
59
+1 para uma pergunta clara, mas, de início, não tenho certeza de que isso seja possível no caso geral, pois um Enumeratoré realmente apenas um invólucro em torno de um StepT => IterateeT, o que sugere que você precisará "renunciar" de um StepT[E, BigMonad, A].
Travis Brown #
12
Sim, descobri isso quando tentei implementá-lo diretamente. Mas logicamente um Enumeratoré apenas uma fonte eficaz, certo? Parece que eu deveria poder usar algo que possa fornecer Apara suprir Task[A].
1
8
Não sei o suficiente sobre o Scala para oferecer uma resposta, mas você não pode definir seu próprio tipo e fornecer um mecanismo de elevação para ele ?
Rob
8
Não, não é a mesma coisa, é um tipo diferente de "levantamento".
Lmm
2
@ TravisBrown, há uma recompensa neste momento, se você quiser escrever.
Aaron Hall

Respostas:

4

Na codificação usual, um enumerador é essencialmente a StepT[E, F, ?] ~> F[StepT[E, F, ?]]. Se você tentar escrever um método genérico que converta esse tipo em um Step[E, G, ?] ~> G[Step[E, G, ?]]dado F ~> G, você encontrará rapidamente um problema: é necessário "diminuir" de a Step[E, G, A]para a Step[E, F, A]para poder aplicar o enumerador original.

O Scalaz também fornece uma codificação de enumerador alternativa que se parece com isso:

trait EnumeratorP[E, F[_]] {
  def apply[G[_]: Monad](f: F ~> G): EnumeratorT[E, G]
}

Essa abordagem nos permite definir um enumerador específico sobre os efeitos de que precisa, mas que pode ser "levantado" para trabalhar com consumidores que exigem contextos mais ricos. Podemos modificar seu exemplo para usar EnumeratorP(e a abordagem mais recente de transformação natural, em vez da antiga ordem parcial da 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) }

Agora podemos compor os dois assim:

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

EnumeratorPé monádico (se o Fé aplicativo) e o EnumeratorPobjeto companheiro fornece algumas funções para ajudar com a definição de recenseadores que se parecem muito com os que estão na EnumeratorT-Não há empty, perform, enumPStream, etc. Eu acho que tem que haver EnumeratorTinstâncias que não poderiam ser implementadas usando a EnumeratorPcodificação, mas no topo da minha cabeça não tenho certeza de como eles seriam.

Travis Brown
fonte