8000 Merge pull request scala/scala#10990 from som-snytt/issue/10370-lazy-… · scala/scala3@7458a28 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7458a28

Browse files
authored
Merge pull request scala/scala#10990 from som-snytt/issue/10370-lazy-list-doc
Boost LazyList Scaladoc just a bit
2 parents 46098b0 + 4a2925a commit 7458a28

File tree

1 file changed

+65
-60
lines changed

1 file changed

+65
-60
lines changed

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

Lines changed: 65 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -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
* // 6DB6 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

Comments
 (0)
0