Scalaz iteratees: “Lifting” `EnumeratorT` untuk mencocokkan` IterateeT` untuk monad “lebih besar”

445

Jika saya punya EnumeratorTdan yang sesuai IterateeTsaya bisa menjalankannya bersama-sama:

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

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

Jika enumerator monad "lebih besar" dari iteratee monad, saya dapat menggunakan upatau, secara umum, Hoistuntuk "mengangkat" iteratee agar sesuai:

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]

Tetapi apa yang harus saya lakukan ketika iteratee monad "lebih besar" daripada enumerator monad?

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

it &= ???

Tampaknya tidak ada Hoistcontoh untuk EnumeratorT, atau metode "lift" yang jelas.

lmm
sumber
59
Memberi +1 untuk pertanyaan yang rapi, tetapi dari atas saya tidak yakin dengan kepalaku bahwa ini mungkin dalam kasus umum, karena sebuah Enumeratorbenar-benar hanya pembungkus di sekitar a StepT => IterateeT, yang menunjukkan bahwa Anda harus "mundur" dari a StepT[E, BigMonad, A].
Travis Brown
12
Ya, saya menemukan itu ketika saya mencoba mengimplementasikannya secara langsung. Tapi secara logis, Enumeratoritu hanya sumber yang efektif, bukan? Rasanya seperti saya harus dapat menggunakan sesuatu yang dapat memasok Auntuk memasok Task[A].
lmm
8
Saya tidak cukup tahu tentang Scala untuk menawarkan jawaban tetapi tidak bisakah Anda mendefinisikan tipe Anda sendiri dan menyediakan mekanisme pengangkatan untuk itu ?
Rob
8
Tidak, itu sama sekali bukan hal yang sama, ini semacam "mengangkat" yang berbeda.
lmm
2
@ TravisBrown ada karunia yang satu ini sekarang, jika Anda ingin menuliskannya.
Aaron Hall

Jawaban:

4

Dalam pengkodean yang biasa, seorang enumerator pada dasarnya adalah a StepT[E, F, ?] ~> F[StepT[E, F, ?]]. Jika Anda mencoba menulis metode umum yang mengubah tipe ini menjadi Step[E, G, ?] ~> G[Step[E, G, ?]]suatu F ~> G, Anda akan dengan cepat mengalami masalah: Anda harus "menurunkan" a Step[E, G, A]ke Step[E, F, A]agar dapat menerapkan enumerator asli.

Scalaz juga menyediakan enumerator alternatif yang terlihat seperti ini:

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

Pendekatan ini memungkinkan kita untuk menentukan enumerator yang spesifik tentang efek yang dibutuhkan, tetapi itu dapat "diangkat" untuk bekerja dengan konsumen yang membutuhkan konteks yang lebih kaya. Kami dapat memodifikasi contoh Anda untuk digunakan EnumeratorP(dan pendekatan transformasi alami yang lebih baru daripada urutan parsial monad lama):

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

Kita sekarang dapat menyusun keduanya seperti ini:

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

EnumeratorPadalah monadik (jika Fyang aplikatif), dan EnumeratorPobjek pendamping menyediakan beberapa fungsi untuk membantu dengan mendefinisikan enumerator yang terlihat banyak seperti yang di EnumeratorTini -ada empty, perform, enumPStream, dll saya kira harus ada EnumeratorTcontoh yang tidak dapat diimplementasikan dengan menggunakan yang EnumeratorPencoding, tetapi dari atas kepala saya, saya tidak yakin apa yang akan mereka terlihat seperti.

Travis Brown
sumber