Somewhat orthogonally, this behavior can be achieved using coroutines. There is at least one library for Scala that enables coroutines, you can find it here: http://storm-enroute.com/coroutines/
Here's an example of the code you'll write to get what you want:
import org.coroutines._
def sideEffectingFunction = coroutine { () =>
val limit = new scala.util.Random().nextInt(10)
val seq = new scala.util.Random
var counter = 0 // mutable state is preserved between coroutine invocations
while (counter < limit) {
counter += 1
yieldval(seq.nextInt)
}
}
defined function sideEffectingFunction
@ val cr = call(sideEffectingFunction())
cr: Coroutine.Frame[Int, Unit] = Coroutine.Frame<depth: 1, live: true>
@ cr.resume
res31: Boolean = true
@ cr.value
res32: Int = 57369026
@ cr.resume
res33: Boolean = true
@ cr.value
res34: Int = -1226825365
@ cr.resume
res35: Boolean = true
@ cr.value
res36: Int = 1304491970
@ cr.resume
res37: Boolean = false
@ cr.value
java.lang.RuntimeException: Coroutine has no value, because it did not yield.
scala.sys.package$.error(package.scala:27)
org.coroutines.Coroutine$Frame$mcI$sp.value$mcI$sp(Coroutine.scala:130)
cmd38$.<init>(Main.scala:196)
cmd38$.<clinit>(Main.scala:-1)
Or, alternatively:
@ val cr = call(sideEffectingFunction())
cr: Coroutine.Frame[Int, Unit] = Coroutine.Frame<depth: 1, live: true>
@ while(cr.resume) println(cr.value)
-1888916682
1135466162
243385373
Or, in spirit of the previous answer:
@ val cr = call(sideEffectingFunction())
cr: Coroutine.Frame[Int, Unit] = Coroutine.Frame<depth: 1, live: true>
@ cr.resume
res60: Boolean = true
@ val iter = Iterator.continually(cr.value).takeWhile(_ => cr.resume)
iter: Iterator[Int] = non-empty iterator
@ iter.foreach(println)
1595200585
995591197
-433181225
220387254
201795229
754208294
-363238006
The advantage of coroutines approach is that you can keep a mutable state between invocations of the underlying side-effecting functions, all nicely hidden from the outer world inside a coroutine. Coroutines can also be composed and invoke each other.
Of course, coroutines give you much more power than just getting your task to work, so this might be an overkill to add them just for this. However, it's a handy technique to be aware of.