When writing a function operating on Stream(s), there are different notions of recursion. The first simple sense is not recursive on the compiler level, since t
A possible workaround is to make the recHelp method not hold reference to the stream head. This can be achieved by passing a wrapped stream to it, and mutating the wrapper to erase the reference from it:
@tailrec
final def rec[A](as: Stream[A]): Stream[B] =
if (a.isEmpty) Stream.empty
else if (someCond) rec(a.tail)
else {
// don't inline and don't define as def,
// or anonymous lazy wrapper object would hold reference
val tailRef = new AtomicReference(a.tail)
someB(a.head) #:: recHelp(tailRef)
}
@tailrec
final def recHelp[A](asRef: AtomicReference[Stream[A]]): Stream[B] =
// Note: don't put the content of the holder into a local variable
rec(asRef.getAndSet(null))
The AtomicReference is just convenience, atomicity is not required in this case, any simple holder object would do.
Also note that since recHelp is wrapped in a stream Cons tail, therefore it will only be evaluated once, and Cons also takes care of synchronization.