@@ -28,49 +28,62 @@ import scala.runtime.Statics
28
28
*
29
29
* Elements are memoized; that is, the value of each element is computed at most once.
30
30
*
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.
33
33
*
34
34
* 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.
38
56
*
39
57
* 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,
41
59
* some methods (such as `count`, `sum`, `max` or `min`) will not terminate.
42
60
*
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:
44
63
*
45
64
* {{{
46
65
* import scala.math.BigInt
47
66
* object Main extends App {
48
67
* 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
+ * }
51
72
* }
52
- *
53
- * // prints
54
- * //
55
- * // 0
56
- * // 1
57
- * // 1
58
- * // 2
59
- * // 3
73
+ * // prints: 0, 1, 1, 2, 3
60
74
* }}}
61
75
*
62
76
* To illustrate, let's add some output to the definition `fibs`, so we
63
77
* see what's going on.
64
78
*
65
79
* {{{
66
80
* import scala.math.BigInt
81
+ * import scala.util.chaining._
67
82
* object Main extends App {
68
83
* val fibs: LazyList[BigInt] =
69
84
* 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")))
74
87
* fibs.take(5).foreach(println)
75
88
* fibs.take(6).foreach(println)
76
89
* }
@@ -79,11 +92,11 @@ import scala.runtime.Statics
79
92
* //
80
93
* // 0
81
94
* // 1
82
- * // Adding 0 and 1
95
+ * // Adding 0 and 1 => 1
83
96
* // 1
84
- * // Adding 1 and 1
97
+ * // Adding 1 and 1 => 2
85
98
* // 2
86
- * // Adding 1 and 2
99
+ * // Adding 1 and 2 => 3
87
100
* // 3
88
101
*
89
102
* // And then prints
@@ -93,35 +106,28 @@ import scala.runtime.Statics
93
106
* //
6DB6
1
94
107
* // 2
95
108
* // 3
96
- * // Adding 2 and 3
109
+ * // Adding 2 and 3 => 5
97
110
* // 5
98
111
* }}}
99
112
*
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.
105
115
*
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.
110
120
*
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.
118
124
*
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.
122
128
*
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 .
125
131
*
126
132
* {{{
127
133
* // We'll start with a silly iteration
@@ -144,10 +150,10 @@ import scala.runtime.Statics
144
150
* val it1 = lazylist1.iterator
145
151
* loop("Iterator1: ", it1.next(), it1)
146
152
*
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.
151
157
* def lazylist2: LazyList[Int] = {
152
158
* def loop(v: Int): LazyList[Int] = v #:: loop(v + 1)
153
159
* loop(0)
@@ -190,19 +196,18 @@ import scala.runtime.Statics
190
196
* }
191
197
* }}}
192
198
*
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.
194
200
* 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
197
203
* evaluated.
198
204
*
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
200
206
* allows LazyList to not eagerly evaluate any elements on a call to `filter`.
201
207
*
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.
204
209
*
205
- * for example:
210
+ * For example:
206
211
*
207
212
* {{{
208
213
* def tailWithSideEffect: LazyList[Nothing] = {
@@ -227,8 +232,8 @@ import scala.runtime.Statics
227
232
* }}}
228
233
*
229
234
* 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:
232
237
*
233
238
* {{{
234
239
* lazy val a: LazyList[Int] = 1 #:: 2 #:: a.filter(_ > 2)
@@ -242,7 +247,7 @@ import scala.runtime.Statics
242
247
* @tparam A the type of the elements contained in this lazy list.
243
248
*
244
249
* @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 .
246
251
* @define Coll `LazyList`
247
252
* @define coll lazy list
248
253
* @define orderDependent
0 commit comments