Scalaz迭代器:将“EnumeratorT”提升以匹配“更大”的单子的“IterateeT”

450

如果我有一个EnumeratorT和相应的IterateeT,我可以将它们一起运行:

val en: EnumeratorT[String, Task] = EnumeratorT.enumList(List("a", "b", "c"))
val it: IterateeT[String, Task, Int] = IterateeT.length

(it &= en).run : Task[Int]

如果枚举器单子比迭代器单子"大",我可以使用up或更一般的Hoist来"提升"迭代器以匹配:

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]

但是当被迭代的单子比枚举器单子更“大”时,我该怎么做?

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

it &= ???

看起来并没有针对 EnumeratorTHoist 实例,也没有明显的“提升”方法。


59
感谢您提供这个整洁的问题。但是,我不能确定在一般情况下是否可能实现,因为Enumerator实际上只是一个围绕StepT => IterateeT的包装器,这表明您需要从StepT[E,BigMonad,A]中“跨越”(step down)来实现。 - Travis Brown
12
当我尝试直接实现它时,我发现无法做到。但是从逻辑上讲,Enumerator 只是一个有副作用的源,对吗?感觉应该可以使用一个能提供 A 的东西来提供 Task[A] - lmm
8
我不了解Scala足够多以提供答案,但是你可以定义自己的类型并为其提供一个提升机制,这样做可能会有所帮助。 - Rob
8
不,那完全不同,它是另一种“举起”的方式。 - lmm
2
@TravisBrown,这个问题目前有赏金,如果你想写一下的话。 - Russia Must Remove Putin
显示剩余2条评论
1个回答

7
在通常的编码中,枚举器本质上是一个StepT[E, F, ?] ~> F[StepT[E, F, ?]]。如果您尝试编写一个通用方法,将此类型转换为Step[E, G, ?] ~> G[Step[E, G, ?]],并给定F ~> G,您很快就会遇到一个问题:您需要将Step[E, G, A]“降低”到Step[E, F, A],以便能够应用原始枚举器。
Scalaz还提供了一种替代的枚举器编码方式,其代码如下:(请参阅)
trait EnumeratorP[E, F[_]] {
  def apply[G[_]: Monad](f: F ~> G): EnumeratorT[E, G]
}

这种方法使我们能够定义一个特定于所需效果的枚举器,但可以将其“提升”以与需要更丰富上下文的消费者一起使用。 我们可以修改您的示例以使用EnumeratorP(以及新的自然变换方法而不是旧的单子偏序):

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

我们现在可以像这样组合这两个元素:
scala> def result = (iter &= enum(toTask)).run
result: scalaz.concurrent.Task[Int]
EnumeratorP 是单子的(如果F是应用类型),并且 EnumeratorP 伴生对象提供了一些函数,以帮助定义枚举器,这些枚举器看起来很像 EnumeratorT 上的枚举器,比如有 emptyperformenumPStream 等等。我猜可能存在无法使用 EnumeratorP 编码实现的 EnumeratorT 实例,但是我还不确定这些实例会是什么样子。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接