Sergey Alaev
Sergey Alaev

Reputation: 3982

Scala future combining

Imagine following variation of InputStream:

trait FutureInputStream {
  //read bytes asynchronously. Empty array means EOF
  def read(): Future[Array[Byte]]
}

Question is how to write discardAll function for such stream? Here is my solution:

//function that discards all input and returns Future completed on EOF
def discardAll(is: FutureInputStream): Future[Unit] = {
  val f = is.read()
  f.flatMap {
    case v if v.length == 0 =>
      Future successful Unit
    case _ =>
      discardAll(is)
  }
}

Obvious problem with this code is non-optimizable recursion: it will quickly run out of stack. Is there more efficient solution?

Upvotes: 2

Views: 121

Answers (1)

Michael Zajac
Michael Zajac

Reputation: 55569

There is nothing wrong with your solution. The call to discardAll(is) is done asynchronously. It doesn't happen in the same stack frame as the previous call, so there will be no stack overflow.

You can kind of see what happens with a naive implementation:

trait FutureInputStream {
     var count = 0
     def read(): Future[Array[Byte]] = {
         if(count < 100000) {
             count += 1
             Future(Array(1))
         } else
             Future(Array())
     }
}

If you were to feed discardAll with an instance of the above, it would be okay.

scala> val is = new FutureInputStream{}
is: FutureInputStream = $anon$1@255d542f

scala> discardAll(is).onComplete { println }
Success(())

Upvotes: 6

Related Questions