Reputation: 923
In my working code is recursion involved. I try to avoid this, because the recursion could be too deep if a predicate don't hold.
case class Chan[T]() {
private var promise: Promise[T] = Promise[T]()
/** Binds a handler with the "write" in casu update() */
def this(handler: (T => Unit) => Unit) {
this
handler(update)
}
def filter(p: (T) => Boolean): Future[T] = {
apply().flatMap(value => if (p(value)) Future(value) else filter(p))
}
def apply(): Future[T] = {
promise = Promise[T]()
promise.future
}
def update(t: T): Unit = {
if (!promise.isCompleted) promise.success(t)
}
}
The problem lays in the filter
method. When the predicate is not met, filter
will be called again, hunting for an event which is conform the parameter. By that the stack will be filled, ahead for a StackOverflow :-).
How can the code refactored, in a loop or tail recursive calls, avoiding excessive stack usage?
Upvotes: 1
Views: 768
Reputation: 925
So here's the solution as suggested above. I hope that fits your purpose:
case class Chan[T]() {
private var pendingFilters: List[(T => Boolean, Promise[T])] = List.empty
/** Binds a handler with the "write" in casu update() */
def this(handler: (T => Unit) => Unit) {
this
handler(update)
}
def filter(p: (T) => Boolean): Future[T] = {
val promise = Promise[T]()
// add both the predicate and the promise to your pending filters
synchronized(pendingFilters = p -> promise :: pendingFilters)
promise.future
}
def update(t: T): Unit = {
synchronized {
// partition your pending filters into completed and uncompleted ones
val (completed, pending) = pendingFilters.partition(_._1(t))
pendingFilters = pending
completed
}.foreach{ case (_, promise) =>
// and finally complete the promises
promise.trySuccess(t)
}
}
}
Upvotes: 1