]> Cypherpunks repositories - gostls13.git/commitdiff
iter: add doc comment from proposal
authorRuss Cox <rsc@golang.org>
Fri, 7 Jun 2024 12:35:32 +0000 (08:35 -0400)
committerGopher Robot <gobot@golang.org>
Fri, 14 Jun 2024 22:40:29 +0000 (22:40 +0000)
Add the doc comment from the proposal, lightly edited.
The edits are to drop mention of value-error Seq2 usage
and to adjust for the bool result changes.

Fixes #61897.

Change-Id: Iaffbaa301064b2f8739956d602ca5fa23c89a269
Reviewed-on: https://go-review.googlesource.com/c/go/+/591096
Auto-Submit: Russ Cox <rsc@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/iter/iter.go

index 30f65f7e4895424f5d33502bfdca24d8fc72ee4a..4ea919b0724f6dbfb7235c52bd2e5128fdc7e56f 100644 (file)
@@ -2,8 +2,186 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package iter provides basic definitions and operations
-// related to iteration in Go.
+/*
+Package iter provides basic definitions and operations related to
+iterators over sequences.
+
+# Iterators
+
+An iterator is a function that passes successive elements of a
+sequence to a callback function, conventionally named yield.
+The function stops either when the sequence is finished or
+when yield returns false, indicating to stop the iteration early.
+This package defines [Seq] and [Seq2]
+(pronounced like seek—the first syllable of sequence)
+as shorthands for iterators that pass 1 or 2 values per sequence element
+to yield:
+
+       type (
+               Seq[V any]     func(yield func(V) bool)
+               Seq2[K, V any] func(yield func(K, V) bool)
+       )
+
+Seq2 represents a sequence of paired values, conventionally key-value
+or index-value pairs.
+
+Yield returns true if the iterator should continue with the next
+element in the sequence, false if it should stop.
+
+Iterator functions are most often called by a range loop, as in:
+
+       func PrintAll[V any](seq iter.Seq[V]) {
+               for _, v := range seq {
+                       fmt.Println(v)
+               }
+       }
+
+# Naming Conventions
+
+Iterator functions and methods are named for the sequence being walked:
+
+       // All returns an iterator over all elements in s.
+       func (s *Set[V]) All() iter.Seq[V]
+
+The iterator method on a collection type is conventionally named All,
+because it iterates a sequence of all the values in the collection.
+
+For a type containing multiple possible sequences, the iterator's name
+can indicate which sequence is being provided:
+
+       // Cities returns an iterator over the major cities in the country.
+       func (c *Country) Cities() iter.Seq[*City]
+
+       // Languages returns an iterator over the official spoken languages of the country.
+       func (c *Country) Languages() iter.Seq[string]
+
+If an iterator requires additional configuration, the constructor function
+can take additional configuration arguments:
+
+       // Scan returns an iterator over key-value pairs with min ≤ key ≤ max.
+       func (m *Map[K, V]) Scan(min, max K) iter.Seq2[K, V]
+
+       // Split returns an iterator over the (possibly-empty) substrings of s
+       // separated by sep.
+       func Split(s, sep string) iter.Seq[string]
+
+When there are multiple possible iteration orders, the method name may
+indicate that order:
+
+       // All returns an iterator over the list from head to tail.
+       func (l *List[V]) All() iter.Seq[V]
+
+       // Backward returns an iterator over the list from tail to head.
+       func (l *List[V]) Backward() iter.Seq[V]
+
+       // Preorder returns an iterator over all nodes of the syntax tree
+       // beneath (and including) the specified root, in depth-first preorder,
+       // visiting a parent node before its children.
+       func Preorder(root Node) iter.Seq[Node]
+
+# Single-Use Iterators
+
+Most iterators provide the ability to walk an entire sequence:
+when called, the iterator does any setup necessary to start the
+sequence, then calls yield on successive elements of the sequence,
+and then cleans up before returning. Calling the iterator again
+walks the sequence again.
+
+Some iterators break that convention, providing the ability to walk a
+sequence only once. These “single-use iterators” typically report values
+from a data stream that cannot be rewound to start over.
+Calling the iterator again after stopping early may continue the
+stream, but calling it again after the sequence is finished will yield
+no values at all, immediately returning true. Doc comments for
+functions or methods that return single-use iterators should document
+this fact:
+
+       // Lines returns an iterator over lines read from r.
+       // It returns a single-use iterator.
+       func (r *Reader) Lines() iter.Seq[string]
+
+# Pulling Values
+
+Functions and methods that accept or return iterators
+should use the standard [Seq] or [Seq2] types, to ensure
+compatibility with range loops and other iterator adapters.
+The standard iterators can be thought of as “push iterators”, which
+push values to the yield function.
+
+Sometimes a range loop is not the most natural way to consume values
+of the sequence. In this case, [Pull] converts a standard push iterator
+to a “pull iterator”, which can be called to pull one value at a time
+from the sequence. [Pull] starts an iterator and returns a pair
+of functions—next and stop—which return the next value from the iterator
+and stop it, respectively.
+
+For example:
+
+       // Pairs returns an iterator over successive pairs of values from seq.
+       func Pairs[V any](seq iter.Seq[V]) iter.Seq2[V, V] {
+               return func(yield func(V, V) bool) bool {
+                       next, stop := iter.Pull(seq)
+                       defer stop()
+                       v1, ok1 := next()
+                       v2, ok2 := next()
+                       for ok1 || ok2 {
+                               if !yield(v1, v2) {
+                                       return false
+                               }
+                       }
+                       return true
+               }
+       }
+
+If clients do not consume the sequence to completion, they must call stop,
+which allows the iterator function to finish and return. As shown in
+the example, the conventional way to ensure this is to use defer.
+
+# Standard Library Usage
+
+A few packages in the standard library provide iterator-based APIs,
+most notably the [maps] and [slices] packages.
+For example, [maps.Keys] returns an iterator over the keys of a map,
+while [slices.Sorted] collects the values of an iterator into a slice,
+sorts them, and returns the slice, so to iterate over the sorted keys of a map:
+
+       for _, key := range slices.Sorted(maps.Keys(m)) {
+               ...
+       }
+
+# Mutation
+
+Iterators provide only the values of the sequence, not any direct way
+to modify it. If an iterator wishes to provide a mechanism for modifying
+a sequence during iteration, the usual approach is to define a position type
+with the extra operations and then provide an iterator over positions.
+
+For example, a tree implementation might provide:
+
+       // Positions returns an iterator over positions in the sequence.
+       func (t *Tree[V]) Positions() iter.Seq[*Pos]
+
+       // A Pos represents a position in the sequence.
+       // It is only valid during the yield call it is passed to.
+       type Pos[V any] struct { ... }
+
+       // Pos returns the value at the cursor.
+       func (p *Pos[V]) Value() V
+
+       // Delete deletes the value at this point in the iteration.
+       func (p *Pos[V]) Delete()
+
+       // Set changes the value v at the cursor.
+       func (p *Pos[V]) Set(v V)
+
+And then a client could delete boring values from the tree using:
+
+       for p := range t.Positions() {
+               if boring(p.Value()) {
+                       p.Delete()
+               }
+       }
+*/
 package iter
 
 import (
@@ -15,11 +193,13 @@ import (
 // Seq is an iterator over sequences of individual values.
 // When called as seq(yield), seq calls yield(v) for each value v in the sequence,
 // stopping early if yield returns false.
+// See the [iter] package documentation for more details.
 type Seq[V any] func(yield func(V) bool)
 
 // Seq2 is an iterator over sequences of pairs of values, most commonly key-value pairs.
 // When called as seq(yield), seq calls yield(k, v) for each pair (k, v) in the sequence,
 // stopping early if yield returns false.
+// See the [iter] package documentation for more details.
 type Seq2[K, V any] func(yield func(K, V) bool)
 
 type coro struct{}
@@ -45,10 +225,13 @@ func coroswitch(*coro)
 // no longer interested in next values and next has not yet
 // signaled that the sequence is over (with a false boolean return).
 // It is valid to call stop multiple times and when next has
-// already returned false.
+// already returned false. Typically, callers should “defer stop()”.
 //
 // It is an error to call next or stop from multiple goroutines
 // simultaneously.
+//
+// If the iterator panics during a call to next (or stop),
+// then next (or stop) itself panics with the same value.
 func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func()) {
        var (
                v          V
@@ -157,10 +340,13 @@ func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func()) {
 // no longer interested in next values and next has not yet
 // signaled that the sequence is over (with a false boolean return).
 // It is valid to call stop multiple times and when next has
-// already returned false.
+// already returned false. Typically, callers should “defer stop()”.
 //
 // It is an error to call next or stop from multiple goroutines
 // simultaneously.
+//
+// If the iterator panics during a call to next (or stop),
+// then next (or stop) itself panics with the same value.
 func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func()) {
        var (
                k          K