Created
August 24, 2016 08:44
-
-
Save ubourdon/b9c24bdb25badac1c8363c7a3d27da12 to your computer and use it in GitHub Desktop.
feed a scalaz Process with recursive async function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| My problem is: | |
| I have a function getData[A](params ...): Task[List[A]] that return asynchronously data | |
| But this data is big so I cut the call in several step playing with params | |
| In term of API, I want expose a function stream[A]: Process[Task, A] | |
| I see Process.repeatEval & stream.async.unboundedQueue API from scalaz | |
| but I don't understand how to use it to produce this result | |
| */ | |
| object Feed[A] { | |
| // return a piece of needed data | |
| private def getData(start: Int, chunk: Int): Task[List[A]] = ??? | |
| // the public API I want to expose | |
| def stream: Process[Task, A] | |
| } |
Author
Author
I think my pb is to exec rec in other thread than q.dequeue, so don't use map function but I don't know how to do this.
Two things:
- No need for queues for something like this (although it would work fine with it, let's not complicate things too much)
- You are doing your recursion at the
Tasklevel, that is, within a singleTask! Remember, aTaskis like aFuture, it only returns a value once, at the end of whatever computation it was doing, hence why you have nothing actually streaming.
This should explain better:
import scalaz._
import scalaz.concurrent.Task
import scalaz.stream.Process
import scalaz.stream.async
import scalaz.stream.async.mutable.Queue
trait Feed[A] {
protected def getData(start: Int, chunk: Int): Task[List[A]]
protected def isEnd(i: Int): Boolean
// we do the recursion at the Process level instead, halting when necessary
private def getChunks(start: Int, chunk: Int): Process[Task, List[A]] =
if (isEnd(start)) Process.halt
else Process.eval(getData(start, start + chunk)) ++ getChunks(start + chunk, chunk)
// let's flatten the feed
def stream: Process[Task, A] = getChunks(0, 250).flatMap(Process.emitAll)
}
object TestFeed extends Feed[Int] {
protected def getData(start: Int, chunk: Int): Task[List[Int]] = Task {
println("debug: getting data!")
Range(start,chunk).toList
}
protected def isEnd(i: Int) = i >= 1000
}
object Run extends App {
// this will output the integer 0 to 999 with "getting data!" interleaved in between
// instead of block of it at the beginning
TestFeed.stream.map(i => print(s"$i;")).run.run
}
Author
Si getData ne renvoi plus que une liste de E mais un résultat plus complexe qui contient notamment la condition de sorti de la récursion.
J'ai tenté ça. J'ai bon ?
trait Feed[A] {
case class ComplexResult[A](result: List[A], nextChunk: Int, endOfData: Boolean)
protected def getData(start: Int, chunk: Int): Task[ComplexResult[A]]
protected def isEnd(i: Int): Boolean
// we do the recursion at the Process level instead, halting when necessary
private def getChunks(start: Int, chunk: Int, end: Boolean): Process[Task, A] =
if (end) Process.halt
else {
val chunk: Task[ReadStreamEventsCompleted] = processChunk(streamUid, nextEventNumber, chunkSize)
chunk.map { result =>
Process.emitAll(readEvent(result)) ++ getChunks(streamUid, result.nextChunk, chunkSize, result.endOfData)
} |> flattenTaskProcess
}
private def flattenTaskProcess[A](f: Task[Process[Task, A]]): Process[Task, A] = Process eval(f) flatMap(x => x)
def stream: Process[Task, A] = getChunks(0, 250, false)
}
Author
When the end of recursion is decide by getData result :
private def getChunks(start: Int, chunk: Int): Process[Task, ComplexResult] =
Process.await(getData(start, start + chunk))({ c: ComplexResult =>
val next = if (c.endOfData) Process.halt else getChunks(start + chunk, chunk)
Process.emit(c) ++ next
})
Author
private def getChunks(start: Int, chunk: Int): Process[Task, ComplexResult] =
Process.await(getData(start, start + chunk))({ c: ComplexResult =>
val next = if (c.endOfData) Process.halt else getChunks(start + chunk, chunk)
Process.emit(c) ++ next
})
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I try this :
The problem is my Process reference is render only when all my call to getData are Done and so I stream nothing here.
Have you any idea how i can peform this ?