To be more specific, I had a code similar to the following one (some of you will probably recognize the pattern of the last assignment of the coursera Functional Programming Course), that failed to find the solution with ++
but worked with #:::
(the set of alreadySeen solution contained a node that had not been explored and prevented the solver from reaching the goal position). I have disguised the code to respect the agreement and in the short sample here ++
leads to a StackOverflowError whereas #:::
yields the good solution. The scala doc for ++
warns about potential inifinite realization when used with other collection but in my example it is used between two streams.
case class PuzzleNode(pos: Position, moves: List[Move])
def reachableNodes(node: PuzzleNode): Stream[PuzzleNode] = ...
def from(initial: Stream[PuzzleNode], alreadySeen: Set[Position]): Stream[PuzzleNode] = {
if (initial.isEmpty) Stream.empty
else {
val next = for {
startNode <- initial
node <- reachableNodes(startNode)
if(!alreadySeen.contains(node.pos))
} yield node
//initial #::: from(next, alreadySeen ++ next.map(_.pos))
initial ++ from(next, alreadySeen ++ next.map(_.pos))
}
}
EDIT:
++
exhibits a lazy behavior for the following code, that I can understand given the ++
implementation in the stream class which uses cons to build the resulting stream.
val fibs: Stream[BigInt] = BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map { n => n._1 + n._2 }
val prefix: Stream[BigInt] = BigInt(4) #:: BigInt(8) #:: Stream.Empty
(prefix ++ fibs) take 10 foreach println
Why is the behavior different in the from
function above ?
++
evaluates its parameter immediately ( call by value), forcing the evaluation of the recursive call tofrom
, which causes theStackOverflow
.#:::
evaluates the parameter lazily ( call by name), so thefrom
recursive call will just happen as the stream is forced.To your edit
++
is not exhibting lazy behaviour, you're calling it with a already definedStream
fibs
, which evaluates only the head. The fact that it doesn't overflow is because the tail is lazily evaluated.To see the difference try the following: