I have a Seq, x, and a Stream, y, and I wish to prepend x to y to obtain a new Stream. However, the static type of y is causing the Stream to be evaluated immediately, and I am confused why this is the case. Here is an example:
val x: Seq[Int] = Seq(1, 2, 3)
val y: Seq[Int] = Stream(4, 5, 6)
val z = x ++: y // z has dynamic type List instead of Stream
Since the ++: method is called on a Stream instance, I expect to get a Stream as a result, but instead I am getting a List as a result. Can someone please explain why this is happening?
tl;dr
it's because of compiler type inference, and when you are using
++:on twoSeqit's just construct anotherSeq.++:creates builder which return type param isSeq, but defaultSeqbuilder ismutable.ListBufferand it's return type isList[A]which is alsoSeq. So, by default it brakes laziness inside builder and return value will beList[Int]but return type will beSeq[Int].Problem investigation
Lets watch to the
++:signature (for example in scala 2.12.10):here we see implicit argument:
bf: CanBuildFrom[Repr, B, That]. In line:here
CanBuildFrom.applycalled, it returnsBuilder[B, That]:When we call
++:on twoSeq[Int]we have defaultCanBuildFromandnewBuilderfor sequences (fromscala.collection.Seq):we see, that newBuilder calls
immutable.Seq.newBuilderfromscala.collection.immutable.Seq:We see
mutable.ListBufferwhich is not lazy.Decision
So, to keep laziness while your concatenation you should pass your own
CanBuildFromforStream[Int], something like that:or you can just make streams from both sequences:
and compiler will find implicit
CanBuildFromfromStreamobject, which is lazy: