Fun with Go Iterators

Go’s new iterator capabilities and third-party libraries are prompting debate over whether the language should embrace more functional, chainable patterns similar to JavaScript, C#, or Rust. Supporters argue that map/filter-style pipelines improve readability, composability, and align with powerful lazy abstractions seen in other ecosystems, while critics say they harm clarity, hide performance costs, and go against Go’s preference for simple loops and explicit control flow. The exchange also touches on generics limitations (like the lack of generic methods), allocation and performance concerns in hot paths, and broader questions about whether Go should evolve toward higher-level abstractions or stay closer to its minimalist, C-like roots.

Libraries and ecosystem

  • Several existing Go libraries offer iterator/collection utilities (e.g., lo, LINQ-style libs, custom iterator packages), so “yet another” chainable-iterator library is seen by some as redundant.
  • Some see value in a clean iterator abstraction using the new iter package; others think it clashes with Go’s design philosophy and will remain niche.

Go generics and missing generic methods

  • A major limitation discussed: Go does not support generic type parameters on methods, which complicates chaining operations that change element type (e.g., Map[string→int] then Map[int→float]).
  • Links to design docs and issues explain this as a trade‑off for compilation speed and Go’s interface model; workarounds exist (extra type parameters, intermediate casts) but are clunky.

Reverse implementation critique

  • The showcased Reverse iterators are criticized for:
    • Traversing the iterator twice and allocating a slice unnecessarily.
    • Failing on single-use iterators.
  • Some argue reverse must materialize data anyway; others suggest type‑specialized or lazy reverse iterators that walk data backward without extra copies.

Functional chaining vs idiomatic Go

  • Strong divide:
    • Pro‑chaining side: finds Filter/Map/Collect pipelines clearer, more declarative, and easier to rearrange than nested for-loops and temporary variables.
    • Anti‑chaining side: sees it as unreadable, “clever,” and against Go’s preference for simple, explicit loops and small named functions.
  • Debate over whether naming every intermediate value improves clarity or just adds noise; examples given for both sides.

Performance and allocations

  • Multiple comments stress that chain-style abstractions often allocate or create intermediate arrays, which is unacceptable in hot or memory-constrained paths.
  • Some report difficulty achieving zero allocations with Go iterators; they fall back to hand-written loops in performance-critical sections.
  • Others argue this is acceptable for most code and that benchmarks can enforce allocation budgets where needed.

Mutation vs purity in iterator wrappers

  • The sample Map mutates the iterator’s internal function, surprising some who expect functional purity.
  • Alternative designs propose returning new iterator values instead of mutating, at the cost of more allocations.