web-dev-qa-db-ja.com

Scalaz iteratees:「より大きい」モナドの「IterateeT」に一致するように「EnumeratorT」を「持ち上げる」

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 &= ???

HoistEnumeratorTインスタンスも、明らかな「リフト」メソッドも存在しないようです。

444
lmm

通常のエンコードでは、列挙子は本質的に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) }

次のように2つを構成できます。

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

EnumeratorPはモナドです(Fが適用可能な場合)、EnumeratorPコンパニオンオブジェクトは、EnumeratorTによく似た列挙子の定義を支援する関数を提供します。 emptyperformenumPStreamなどがあります。EnumeratorTエンコードを使用して実装できなかったEnumeratorPインスタンスが存在する必要があると思いますが、オフ私の頭の上では、彼らがどのように見えるかわかりません。

4
Travis Brown