Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Turn a side-effecting function returning Option into an Iterator

Assume we have a side-effecting "producer" function f: () => Option[T] which returns a Some when repeatedly called until a future point at which it will forever return None. (e.g. a wrapped Java API producing null at EOF might well have this sort of behaviour).

Is it possible to wrap this function into something like a TraversableOnce or an Iterator, with the following constraints:

  • Standard Scala library constructs preferred
  • The sequence could be arbitrarily long and holding all the values, e.g. in a Stream is not wanted
  • Similarly there must be no possibility of stack overflow
  • The visible user source code should not use a var
  • Thread safety is not required

There are some useful methods on the Iterator object, but nothing that exactly matches my use case. Any ideas welcome!

like image 720
satyagraha Avatar asked Feb 22 '16 10:02

satyagraha


People also ask

What is iterator in Scala?

An iterator is a way to access elements of a collection one-by-one. It resembles to a collection in terms of syntax but works differently in terms of functionality. An iterator defined for any collection does not load the entire collection into the memory but loads elements one after the other.

Is Scala iterator lazy?

Unlike operations directly on a concrete collection like List , operations on Iterator are lazy. A lazy operation does not immediately compute all of its results.


Video Answer


1 Answers

This does the trick:

def wrap[T](f: () => Option[T]): Iterator[T] = {
  Iterator.continually(f()).takeWhile(_.isDefined).flatten      
}

REPL test:

scala> :paste
// Entering paste mode (ctrl-D to finish)
var i = 0
def sideEffectingFunc(): Option[Int] = {
  i += 1
  if (i < 10) Some(i)
  else None
}
// Exiting paste mode, now interpreting.

i: Int = 0
sideEffectingFunc: ()Option[Int]

scala> val it = wrap(sideEffectingFunc)
it: Iterator[Int] = non-empty iterator

scala> it.toList
res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
like image 84
Régis Jean-Gilles Avatar answered Sep 29 '22 17:09

Régis Jean-Gilles