Q:

restart iterator on exceptions in Scala

I have an iterator (actually a Source.getLines) that's reading an infinite stream of data from a URL. Occasionally the iterator throws a java.io.IOException when there is a connection problem. In such situations, I need to re-connect and re-start the iterator. I want this to be seamless so that the iterator just looks like a normal iterator to the consumer, but underneath is restarting itself as necessary.
For example, I'd like to see the following behavior:
scala> val iter = restartingIterator(() => new Iterator[Int]{
  var i = -1
  def hasNext = {
    if (this.i < 3) {
      true
    } else {
      throw new IOException
    }
  }
  def next = {
    this.i += 1
    i
  }
})
res0: ...

scala> iter.take(6).toList
res1: List[Int] = List(0, 1, 2, 3, 0, 1)

I have a partial solution to this problem, but it will fail on some corner cases (e.g. an IOException on the first item after a restart) and it's pretty ugly:
def restartingIterator[T](getIter: () => Iterator[T]) = new Iterator[T] {
  var iter = getIter()
  def hasNext = {
    try {
      iter.hasNext
    } catch {
      case e: IOException => {
        this.iter = getIter()
        iter.hasNext
      }
    }
  }
  def next = {
    try {
      iter.next
    } catch {
      case e: IOException => {
        this.iter = getIter()
        iter.next
      }
    }
  }
}

I keep feeling like there's a better solution to this, maybe some combination of Iterator.continually and util.control.Exception or something like that, but I couldn't figure one out. Any ideas?

A:

This is fairly close to your version and using scala.util.control.Exception:
def restartingIterator[T](getIter: () => Iterator[T]) = new Iterator[T] {
  import util.control.Exception.allCatch
  private[this] var i = getIter()
  private[this] def replace() = i = getIter()
  def hasNext: Boolean = allCatch.opt(i.hasNext).getOrElse{replace(); hasNext}
  def next(): T = allCatch.opt(i.next).getOrElse{replace(); next}
}

For some reason this is not tail recursive but it that can be fixed by using a slightly more verbose version:
def restartingIterator2[T](getIter: () => Iterator[T]) = new Iterator[T] {
  import util.control.Exception.allCatch
  private[this] var i = getIter()
  private[this] def replace() = i = getIter()
  @annotation.tailrec def hasNext: Boolean = {
    val v = allCatch.opt(i.hasNext)
    if (v.isDefined) v.get else {replace(); hasNext}
  }
  @annotation.tailrec def next(): T = {
    val v = allCatch.opt(i.next)
    if (v.isDefined) v.get else {replace(); next}
  }
}

Edit: There is a solution with util.control.Exception and Iterator.continually:
def restartingIterator[T](getIter: () => Iterator[T]) = {
  import util.control.Exception.allCatch
  var iter = getIter()
  def f: T = allCatch.opt(iter.next).getOrElse{iter = getIter(); f}
  Iterator.continually { f }
}

