Skip to content

Commit f4003ac

Browse files
committed
Stop Stream from sharing implementation with LazyList
Extract Stream into its own file, and inline methods and implementation into Stream or its companion object.
1 parent 066bad6 commit f4003ac

File tree

2 files changed

+521
-186
lines changed

2 files changed

+521
-186
lines changed

library/src/scala/collection/immutable/LazyList.scala

Lines changed: 0 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -693,189 +693,3 @@ object LazyList extends LazyListFactory[LazyList] {
693693
private[this] def writeObject(out: ObjectOutputStream): Unit = ()
694694
private[this] def readObject(in: ObjectInputStream): Unit = ()
695695
}
696-
697-
@deprecated("Use LazyList (which has a lazy head and tail) instead of Stream (which has a lazy tail only)", "2.13.0")
698-
@SerialVersionUID(3L)
699-
sealed abstract class Stream[+A] extends AbstractSeq[A] with LinearSeq[A] with LazyListOps[A, Stream, Stream[A]] {
700-
override def iterableFactory: LazyListFactory[Stream] = Stream
701-
702-
override protected[this] def className: String = "Stream"
703-
704-
protected def cons[T](hd: => T, tl: => Stream[T]): Stream[T] = new Stream.Cons(hd, tl)
705-
706-
/** Apply the given function `f` to each element of this linear sequence
707-
* (while respecting the order of the elements).
708-
*
709-
* @param f The treatment to apply to each element.
710-
* @note Overridden here as final to trigger tail-call optimization, which
711-
* replaces 'this' with 'tail' at each iteration. This is absolutely
712-
* necessary for allowing the GC to collect the underlying LazyList as elements
713-
* are consumed.
714-
* @note This function will force the realization of the entire LazyList
715-
* unless the `f` throws an exception.
716-
*/
717-
@tailrec
718-
override final def foreach[U](f: A => U): Unit = {
719-
if (!this.isEmpty) {
720-
f(head)
721-
tail.foreach(f)
722-
}
723-
}
724-
725-
override def take(n: Int): Stream[A] = {
726-
if (n <= 0 || isEmpty) Stream.empty
727-
else if (n == 1) new Stream.Cons(head, Stream.empty)
728-
else new Stream.Cons(head, tail.take(n - 1))
729-
}
730-
731-
/** LazyList specialization of foldLeft which allows GC to collect along the
732-
* way.
733-
*
734-
* @tparam B The type of value being accumulated.
735-
* @param z The initial value seeded into the function `op`.
736-
* @param op The operation to perform on successive elements of the `LazyList`.
737-
* @return The accumulated value from successive applications of `op`.
738-
*/
739-
@tailrec
740-
override final def foldLeft[B](z: B)(op: (B, A) => B): B = {
741-
if (this.isEmpty) z
742-
else tail.foldLeft(op(z, head))(op)
743-
}
744-
745-
@deprecated("The `append` operation has been renamed `lazyAppendedAll`", "2.13.0")
746-
@inline final def append[B >: A](suffix: IterableOnce[B]): Stream[B] = lazyAppendedAll(suffix)
747-
748-
override protected[this] def writeReplace(): AnyRef =
749-
if(headDefined && tailDefined) new LazyListOps.SerializationProxy[A, Stream](this) else this
750-
751-
/** Prints elements of this stream one by one, separated by commas. */
752-
@deprecated(message = """Use print(stream.force.mkString(", ")) instead""", since = "2.13.0")
753-
@inline def print(): Unit = Console.print(this.force.mkString(", "))
754-
755-
/** Prints elements of this stream one by one, separated by `sep`.
756-
* @param sep The separator string printed between consecutive elements.
757-
*/
758-
@deprecated(message = "Use print(stream.force.mkString(sep)) instead", since = "2.13.0")
759-
@inline def print(sep: String): Unit = Console.print(this.force.mkString(sep))
760-
761-
}
762-
763-
@deprecated("Use LazyList (which has a lazy head and tail) instead of Stream (which has a lazy tail only)", "2.13.0")
764-
@SerialVersionUID(3L)
765-
object Stream extends LazyListFactory[Stream] {
766-
767-
protected def newCons[T](hd: => T, tl: => Stream[T]): Stream[T] = new Stream.Cons(hd, tl)
768-
769-
//@SerialVersionUID(3L) //TODO Putting an annotation on Stream.empty causes a cyclic dependency in unpickling
770-
object Empty extends Stream[Nothing] {
771-
override def isEmpty: Boolean = true
772-
override def head: Nothing = throw new NoSuchElementException("head of empty stream")
773-
override def tail: Stream[Nothing] = throw new UnsupportedOperationException("tail of empty stream")
774-
/** Forces evaluation of the whole `Stream` and returns it.
775-
*
776-
* @note Often we use `Stream`s to represent an infinite set or series. If
777-
* that's the case for your particular `Stream` then this function will never
778-
* return and will probably crash the VM with an `OutOfMemory` exception.
779-
* This function will not hang on a finite cycle, however.
780-
*
781-
* @return The fully realized `Stream`.
782-
*/
783-
def force: this.type = this
784-
override def knownSize: Int = 0
785-
override def iterator: Iterator[Nothing] = Iterator.empty
786-
protected def headDefined: Boolean = false
787-
protected def tailDefined: Boolean = false
788-
}
789-
790-
@SerialVersionUID(3L)
791-
final class Cons[A](override val head: A, tl: => Stream[A]) extends Stream[A] {
792-
private[this] var tlEvaluated: Boolean = false
793-
override def isEmpty: Boolean = false
794-
override lazy val tail: Stream[A] = {
795-
tlEvaluated = true
796-
tl
797-
}
798-
protected def headDefined: Boolean = true
799-
protected def tailDefined: Boolean = tlEvaluated
800-
/** Forces evaluation of the whole `Stream` and returns it.
801-
*
802-
* @note Often we use `Stream`s to represent an infinite set or series. If
803-
* that's the case for your particular `Stream` then this function will never
804-
* return and will probably crash the VM with an `OutOfMemory` exception.
805-
* This function will not hang on a finite cycle, however.
806-
*
807-
* @return The fully realized `Stream`.
808-
*/
809-
def force: this.type = {
810-
// Use standard 2x 1x iterator trick for cycle detection ("those" is slow one)
811-
var these, those: Stream[A] = this
812-
if (!these.isEmpty) these = these.tail
813-
while (those ne these) {
814-
if (these.isEmpty) return this
815-
these = these.tail
816-
if (these.isEmpty) return this
817-
these = these.tail
818-
if (these eq those) return this
819-
those = those.tail
820-
}
821-
this
822-
}
823-
824-
}
825-
826-
/** An alternative way of building and matching Streams using Stream.cons(hd, tl).
827-
*/
828-
object cons {
829-
/** A stream consisting of a given first element and remaining elements
830-
* @param hd The first element of the result stream
831-
* @param tl The remaining elements of the result stream
832-
*/
833-
def apply[A](hd: A, tl: => Stream[A]): Stream[A] = new Cons(hd, tl)
834-
835-
/** Maps a stream to its head and tail */
836-
def unapply[A](xs: Stream[A]): Option[(A, Stream[A])] = #::.unapply(xs)
837-
}
838-
839-
implicit def toDeferrer[A](l: => Stream[A]): Deferrer[A] = new Deferrer[A](() => l)
840-
841-
final class Deferrer[A] private[Stream] (private val l: () => Stream[A]) extends AnyVal {
842-
/** Construct a Stream consisting of a given first element followed by elements
843-
* from another Stream.
844-
*/
845-
def #:: [B >: A](elem: B): Stream[B] = new Cons(elem, l())
846-
/** Construct a Stream consisting of the concatenation of the given Stream and
847-
* another Stream.
848-
*/
849-
def #:::[B >: A](prefix: Stream[B]): Stream[B] = prefix lazyAppendedAll l()
850-
}
851-
852-
object #:: {
853-
def unapply[A](s: Stream[A]): Option[(A, Stream[A])] =
854-
if (s.nonEmpty) Some((s.head, s.tail)) else None
855-
}
856-
857-
def from[A](coll: collection.IterableOnce[A]): Stream[A] = coll match {
858-
case coll: Stream[A] => coll
859-
case _ => fromIterator(coll.iterator)
860-
}
861-
862-
/**
863-
* @return A `Stream[A]` that gets its elements from the given `Iterator`.
864-
*
865-
* @param it Source iterator
866-
* @tparam A type of elements
867-
*/
868-
// Note that the resulting `Stream` will be effectively iterable more than once because
869-
// `Stream` memoizes its elements
870-
def fromIterator[A](it: Iterator[A]): Stream[A] =
871-
if (it.hasNext) {
872-
new Stream.Cons(it.next(), fromIterator(it))
873-
} else Stream.Empty
874-
875-
def empty[A]: Stream[A] = Empty
876-
877-
// scalac generates a `readReplace` method to discard the deserialized state (see https://github.com/scala/bug/issues/10412).
878-
// This prevents it from serializing it in the first place:
879-
private[this] def writeObject(out: ObjectOutputStream): Unit = ()
880-
private[this] def readObject(in: ObjectInputStream): Unit = ()
881-
}

0 commit comments

Comments
 (0)