Epicurist
Epicurist

Reputation: 923

Tackling recursion while using Scala Futures

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

Answers (1)

Moritz
Moritz

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

Related Questions