Reputation: 41909
Here's my attempt at Functional Programming in Scala's exercise to write toList
for the Stream
class.
def toList[A](stream: Stream[A]): List[A] = {
def go(s: Stream[A], acc: List[A]): List[A] = s match {
case x #:: xs => go(xs, acc :+ x)
case _ => acc
}
go(stream, Nil)
}
Based on reading (but not understanding all) of this post, I was unsure if my pattern matching was correct. In particular, I was concerned that my first case was resulting in immediately evaluating the tail of stream.
Conceptually, I think that I need to implement toList
where each recursion step adds the head of the stream to the list, not evaluating the tail for each step.
Am I correct in this understanding and the above implementation?
Upvotes: 2
Views: 361
Reputation: 20285
The docs and source for Stream
addresses this pretty well.
The source for #::
is:
object #:: {
def unapply[A](xs: Stream[A]): Option[(A, Stream[A])] =
if (xs.isEmpty) None
else Some((xs.head, xs.tail))
}
The relevant part of this is Some((xs.head, xs.tail))
. The source for tail
states that:
Note that this method does not force evaluation of the
Stream
but merely returns the lazy result.
Based on the docs xs
does not get evaluated in the case statement case x #:: xs
. If this case matches, then go(xs, acc :+ x)
is called. xs
here is essentially s.tail
which is not evaluated as noted above.
The code in the post you linked to is different in that tail( a Stream
) is used in recursive calls to merge
and is then unapply
is called on it destructuring it into it's head and tail. Since the head
is a Stream
it gets evaluated and will stack overflow on large enough inputs. @Didier Dupont states this nicely too:
Note also that in Scala Stream, while the tail is lazy, the head is not. When you have a (non empty) Stream, the head has to be known. Which means that when you get the tail of the stream, itself a stream, its head, that is the second element of the original stream, has to be computed. This is sometimes problematic, but in your example, you fail before even getting there.
The extraction of head
and tail
for xs
in your code won't have this issue unless A
is a Stream
. i.e. a stream of streams. The code in the other post creates this situation via the recursive call merge
.
Upvotes: 4