@@ -28,49 +28,62 @@ import scala.runtime.Statics
2828 *
2929 * Elements are memoized; that is, the value of each element is computed at most once.
3030 *
31- * Elements are computed in- order and are never skipped. In other words,
32- * accessing the tail causes the head to be computed first.
31+ * Elements are computed in order and are never skipped.
32+ * As a consequence, accessing the tail causes the head to be computed first.
3333 *
3434 * How lazy is a `LazyList`? When you have a value of type `LazyList`, you
35- * don't know yet whether the list is empty or not. If you learn that it is non-empty,
36- * then you also know that the head has been computed. But the tail is itself
37- * a `LazyList`, whose emptiness-or-not might remain undetermined.
35+ * don't know yet whether the list is empty.
36+ * We say that it is lazy in its head.
37+ * If you have tested that it is non-empty,
38+ * then you also know that the head has been computed.
39+ *
40+ * It is also lazy in its tail, which is also a `LazyList`.
41+ * You don't know whether the tail is empty until it is "forced", which is to say,
42+ * until an element of the tail is computed.
43+ *
44+ * These important properties of `LazyList` depend on its construction using `#::` (or `#:::`).
45+ * That operator is analogous to the "cons" of a strict `List`, `::`.
46+ * It is "right-associative", so that the collection goes on the "right",
47+ * and the element on the left of the operator is prepended to the collection.
48+ * However, unlike the cons of a strict `List`, `#::` is lazy in its parameter,
49+ * which is the element prepended to the left, and also lazy in its right-hand side,
50+ * which is the `LazyList` being prepended to.
51+ * (That is accomplished by implicitly wrapping the `LazyList`, as shown in the Scaladoc.)
52+ *
53+ * Other combinators from the collections API do not preserve this laziness.
54+ * In particular, `++`, or `concat`, is "eager" or "strict" in its parameter
55+ * and should not be used to compose `LazyList`s.
3856 *
3957 * A `LazyList` may be infinite. For example, `LazyList.from(0)` contains
40- * all of the natural numbers 0, 1, 2, and so on . For infinite sequences,
58+ * all of the natural numbers `0`, `1`, `2`, .. . For infinite sequences,
4159 * some methods (such as `count`, `sum`, `max` or `min`) will not terminate.
4260 *
43- * Here is an example:
61+ * Here is an example showing the Fibonacci sequence,
62+ * which may be evaluated to an arbitrary number of elements:
4463 *
4564 * {{{
4665 * import scala.math.BigInt
4766 * object Main extends App {
4867 * val fibs: LazyList[BigInt] =
49- * BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map{ n => n._1 + n._2 }
50- * fibs.take(5).foreach(println)
68+ * BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map(n => n._1 + n._2)
69+ * println {
70+ * fibs.take(5).mkString(", ")
71+ * }
5172 * }
52- *
53- * // prints
54- * //
55- * // 0
56- * // 1
57- * // 1
58- * // 2
59- * // 3
73+ * // prints: 0, 1, 1, 2, 3
6074 * }}}
6175 *
6276 * To illustrate, let's add some output to the definition `fibs`, so we
6377 * see what's going on.
6478 *
6579 * {{{
6680 * import scala.math.BigInt
81+ * import scala.util.chaining._
6782 * object Main extends App {
6883 * val fibs: LazyList[BigInt] =
6984 * BigInt(0) #:: BigInt(1) #::
70- * fibs.zip(fibs.tail).map{ n =>
71- * println(s"Adding \${n._1} and \${n._2}")
72- * n._1 + n._2
73- * }
85+ * fibs.zip(fibs.tail).map(n => (n._1 + n._2)
86+ * .tap(sum => println(s"Adding ${n._1} and ${n._2} => $sum")))
7487 * fibs.take(5).foreach(println)
7588 * fibs.take(6).foreach(println)
7689 * }
@@ -79,11 +92,11 @@ import scala.runtime.Statics
7992 * //
8093 * // 0
8194 * // 1
82- * // Adding 0 and 1
95+ * // Adding 0 and 1 => 1
8396 * // 1
84- * // Adding 1 and 1
97+ * // Adding 1 and 1 => 2
8598 * // 2
86- * // Adding 1 and 2
99+ * // Adding 1 and 2 => 3
87100 * // 3
88101 *
89102 * // And then prints
@@ -93,35 +106,28 @@ import scala.runtime.Statics
93106 * // 1
94107 * // 2
95108 * // 3
96- * // Adding 2 and 3
109+ * // Adding 2 and 3 => 5
97110 * // 5
98111 * }}}
99112 *
100- * Note that the definition of `fibs` uses `val` not `def`. The memoization of the
101- * `LazyList` requires us to have somewhere to store the information and a `val`
102- * allows us to do that.
103- *
104- * Further remarks about the semantics of `LazyList`:
113+ * Note that the definition of `fibs` uses `val` not `def`.
114+ * Memoization of the `LazyList` requires us to retain a reference to the computed values.
105115 *
106- * - Though the `LazyList` changes as it is accessed, this does not
107- * contradict its immutability. Once the values are memoized they do
108- * not change. Values that have yet to be memoized still "exist", they
109- * simply haven't been computed yet.
116+ * `LazyList` is considered an immutable data structure, even though its elements are computed on demand.
117+ * Once the values are memoized they do not change.
118+ * Moreover, the `LazyList` itself is defined once and references to it are interchangeable.
119+ * Values that have yet to be memoized still "exist"; they simply haven't been computed yet.
110120 *
111- * - One must be cautious of memoization; it can eat up memory if you're not
112- * careful. That's because memoization of the `LazyList` creates a structure much like
113- * [[scala.collection.immutable.List ]]. As long as something is holding on to
114- * the head, the head holds on to the tail, and so on recursively.
115- * If, on the other hand, there is nothing holding on to the head (e.g. if we used
116- * `def` to define the `LazyList`) then once it is no longer being used directly,
117- * it disappears.
121+ * Memoization can be a source of memory leaks and must be used with caution.
122+ * It avoids recomputing elements of the list, but if a reference to the head
123+ * is retained unintentionally, then all elements will be retained.
118124 *
119- * - Note that some operations, including [[ drop ]], [[ dropWhile ]],
120- * [[ flatMap ]] or [[collect ]] may process a large number of intermediate
121- * elements before returning.
125+ * The caveat that all elements are computed in order means
126+ * that some operations, such as [[ drop ]], [[ dropWhile ]], [[ flatMap ]] or [[collect ]],
127+ * may process a large number of intermediate elements before returning.
122128 *
123- * Here's another example. Let's start with the natural numbers and iterate
124- * over them .
129+ * Here's an example that illustrates these behaviors.
130+ * Let's begin with an iteration of the natural numbers .
125131 *
126132 * {{{
127133 * // We'll start with a silly iteration
@@ -144,10 +150,10 @@ import scala.runtime.Statics
144150 * val it1 = lazylist1.iterator
145151 * loop("Iterator1: ", it1.next(), it1)
146152 *
147- * // We can redefine this LazyList such that all we have is the Iterator left
148- * // and allow the LazyList to be garbage collected as required. Using a def
149- * // to provide the LazyList ensures that no val is holding onto the head as
150- * // is the case with lazylist1
153+ * // We can redefine this LazyList such that we retain only a reference to its Iterator.
154+ * // That allows the LazyList to be garbage collected.
155+ * // Using `def` to produce the LazyList in a method ensures
156+ * // that no val is holding onto the head, as with lazylist1.
151157 * def lazylist2: LazyList[Int] = {
152158 * def loop(v: Int): LazyList[Int] = v #:: loop(v + 1)
153159 * loop(0)
@@ -190,19 +196,18 @@ import scala.runtime.Statics
190196 * }
191197 * }}}
192198 *
193- * The head, the tail and whether the list is empty or not can be initially unknown.
199+ * The head, the tail and whether the list is empty is initially unknown.
194200 * Once any of those are evaluated, they are all known, though if the tail is
195- * built with `#::` or `#:::`, it's content still isn't evaluated. Instead, evaluating
196- * the tails content is deferred until the tails empty status, head or tail is
201+ * built with `#::` or `#:::`, its content still isn't evaluated. Instead, evaluating
202+ * the tail's content is deferred until the tail's empty status, head or tail is
197203 * evaluated.
198204 *
199- * Delaying the evaluation of whether a LazyList is empty or not until it's needed
205+ * Delaying the evaluation of whether a LazyList is empty until it's needed
200206 * allows LazyList to not eagerly evaluate any elements on a call to `filter`.
201207 *
202- * Only when it's further evaluated (which may be never!) any of the elements gets
203- * forced.
208+ * Only when it's further evaluated (which may be never!) do any of the elements get forced.
204209 *
205- * for example:
210+ * For example:
206211 *
207212 * {{{
208213 * def tailWithSideEffect: LazyList[Nothing] = {
@@ -227,8 +232,8 @@ import scala.runtime.Statics
227232 * }}}
228233 *
229234 * This exception occurs when a `LazyList` is attempting to derive its next element
230- * from itself, and is attempting to read the element currently being evaluated. A
231- * trivial example of such might be
235+ * from itself, and is attempting to read the element currently being evaluated.
236+ * As a trivial example:
232237 *
233238 * {{{
234239 * lazy val a: LazyList[Int] = 1 #:: 2 #:: a.filter(_ > 2)
@@ -242,7 +247,7 @@ import scala.runtime.Statics
242247 * @tparam A the type of the elements contained in this lazy list.
243248 *
244249 * @see [[https://docs.scala-lang.org/overviews/collections-2.13/concrete-immutable-collection-classes.html#lazylists "Scala's Collection Library overview" ]]
245- * section on `LazyLists` for more information .
250+ * section on `LazyLists` for a summary .
246251 * @define Coll `LazyList`
247252 * @define coll lazy list
248253 * @define orderDependent
0 commit comments