Scalaz iteratees: "Піднімаючи" `EnumeratorT`, щоб відповідати` IterateeT` для "більшої" монади


445

Якщо у мене є 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 &= ???

Здається , не існує жодного Hoistпримірника для EnumeratorTжодного явного методу "зняття".


59
+1 для акуратного питання, але вгорі я не впевнений, що це можливо в загальному випадку, оскільки Enumeratorце справді просто обгортка навколо StepT => IterateeT, що говорить про те, що вам потрібно буде "відступити" з а StepT[E, BigMonad, A].
Тревіс Браун

12
Так, я виявив, що намагався реалізувати це безпосередньо. Але логічно, Enumeratorце лише ефективне джерело, правда? Складається враження, що я повинен мати можливість використовувати річ, яка може поставити її Aдля постачання Task[A].
lmm

8
Я не знаю достатньо про Scala, щоб запропонувати відповідь, але чи не могли ви визначити власний тип та забезпечити механізм підйому для цього ?
Роб

8
Ні, це зовсім не те саме, це різний вид "підняття".
lmm

2
@TravisBrown зараз на цьому є щедрість, якщо ви хочете написати це.
Aaron Hall

Відповіді:


4

У звичайному кодуванні нумератор по суті є a StepT[E, F, ?] ~> F[StepT[E, F, ?]]. Якщо ви спробуєте написати загальний метод перетворення цього типу в Step[E, G, ?] ~> G[Step[E, G, ?]]заданий F ~> G, ви швидко зіткнетеся з проблемою: вам потрібно "опустити" a Step[E, G, A]на 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є empty, perform- enumPStream, і т. д. Я думаю, повинні бути EnumeratorTвипадки, які не вдалося реалізувати за допомогою EnumeratorPкодування, але з верхньої частини моєї голови , я не впевнений , що вони будуть виглядати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.