Why People are Angry over Go 1.23 Iterators
-
Context:
- Most languages provide standardized way to iterate over values stored in containers.
-
Problem:
-
Go has for range for maps, slices, strings, arrays and channels but no generic mechanism for user-written containers.
-
Short list of some non-generic iterators:
-
runtime.CallersFrames: returns runtime.Frames iterates over stack frames; Frames has a Next method and a bool to check if there are more frames.
-
bufio.Scanner: iterates through an io.Reader; Scan method advances to the next value, Bytes method to return the value and Err to return error.
-
database/sql.Rows: iterates through the results of a query; also has Scan method.
-
-
Before Generics were introduced, no way to write an interface that described an iterator that would cover all of the use cases...
-
-
Solution:
- Go 1.22 proposal of adding package iter can range over integers and functions:
type ( Seq[V any] func(yield func(V) bool) bool Seq2[K, V any] func(yield func(K, V) bool) bool )
(Seq2 represents a sequence of paired values: key-value, index-value or value-error)
- An iterator is a function that passes successive elements of a sequence to a callback function yield, it returns true if it should continue, false if it should stop.
type ( Yield[V any] func(V bool) Yield2[K, V any] func(K,V) bool )
- Examples:
func Backward[E any](s []E) func(func(int, E) bool) { return func(yield func(int, E) bool) { for i := len(s)-1; i >= 0; i-- { if !yield(i, s[i]) { // Where clean-up code goes return } } } } s := []string{"a", "b", "c"} for _, el in range Backward(s) { fmt.Print(el, " ") } // c b a
-
Conclusion:
- The for range is getting complex:
- return true => continue
- return false => break
- Feels too functional than an imperative language
- Still the purpose is good, let's wait to see the adoption in community after awhile
- The for range is getting complex: