xiter is an extension toolkit for Go's standard iter package. It provides functional, lazy, and type-safe sequence operations for both iter.Seq[E] and iter.Seq2[K, V].
- Features
- Installation
- Quick Start
- Core Concepts
- API Overview
- Best Practices
- Development & Testing
- Roadmap
- License
- ✅ Supports both
iter.Seq[E]anditer.Seq2[K, V] - ✅ Fully generic and type-safe
- ✅ Lazy evaluation with on-demand consumption
- ✅ Covers common workflows: map, filter, slice, reduce, search, compare, sorted checks
- ✅ Supports error-aware terminal operations (
TryForEach,TryFold,TryReduce) - ✅ Provides both functional APIs (
xiter) and fluent APIs (xiter/stream) - ✅ Includes source generators:
Range,Iterate,FromFunc,Once,Empty,Repeat - 🧪 Experimental
collectorsubpackage with reusable terminal collectors (ToSlice,ToMap,GroupingBy, ...)
go get github.com/go-board/xiterpackage main
import (
"fmt"
"github.com/go-board/xiter"
)
func main() {
numbers := xiter.Range1(10) // 0..9
evens := xiter.Filter(numbers, func(v int) bool { return v%2 == 0 })
doubled := xiter.Map(evens, func(v int) int { return v * 2 })
sum := xiter.Fold(doubled, 0, func(acc, v int) int { return acc + v })
fmt.Println(sum) // 40
}
streamprovides chainableSeq/Seq2function types. Core same-type methods work without method-level generics. Methods that need their own type parameters, such asMap,Fold,Split,Join,Zip, andZipWith, require Go 1.27 or newer.
package main
import (
"fmt"
"github.com/go-board/xiter"
"github.com/go-board/xiter/stream"
)
func main() {
s := stream.Of(xiter.Range1(10)).
Skip(2).
Take(5).
Filter(func(v int) bool { return v%2 == 0 })
result := make([]int, 0)
s.ForEach(func(v int) { result = append(result, v*10) })
fmt.Println(result) // [20 40 60]
}With Go 1.27 generic methods, stream also supports type-changing chains:
s := stream.Of(xiter.Range1(5)).
Map(func(v int) string { return fmt.Sprintf("n=%d", v) }).
Fold([]string{}, func(acc []string, v string) []string {
return append(acc, v)
})
fmt.Println(s) // [n=0 n=1 n=2 n=3 n=4]Iterate generates a sequence from a seed and a step function that returns
(next, ok); the sequence stops when ok is false, so no external limiter is
needed:
// 1, 2, 4, 8, 16 — stops once x reaches 16.
stream.Iterate(1, func(x int) (int, bool) {
if x >= 16 {
return 0, false
}
return x * 2, true
}).ForEach(func(v int) { fmt.Println(v) })iter.Seq[E]: sequence of single valuesiter.Seq2[K, V]: sequence of key/value pairsstream.Seq[E]andstream.Seq2[K, V]: chainable wrappers over the bare iterator function types (func(yield func(E) bool)/func(yield func(K, V) bool)), the same underlying type asiter.Seq/iter.Seq2- Sequences are lazy: execution happens at terminal stages like
ForEach,Fold,First, andLast - Sequences are usually single-pass: avoid re-consuming the same exhausted source
See full signatures on GoDoc: https://pkg.go.dev/github.com/go-board/xiter
Range1,Range2,Range3FromFunc,FromFunc2Iterate,Iterate2Once,Once2Empty,Empty2Repeat,Repeat2
Map,Map2MapWhile,MapWhile2FlatMap,FlattenInspect,Inspect2EnumerateJoin,SplitKeys,Values,SwapCastScan
Filter,Filter2FilterMap,FilterMap2Take,Take2,TakeWhile,TakeWhile2Skip,Skip2,SkipWhile,SkipWhile2StepBy,StepBy2Chain,Chain2Zip,ZipWithCompact,CompactFunc,Compact2,CompactFunc2
ForEach,ForEach2TryForEach,TryForEach2Fold,Fold2,TryFold,TryFold2Reduce,Reduce2,TryReduce,TryReduce2Size,Size2,SizeFunc,SizeFunc2,SizeValue,SizeValue2
Contains,Contains2,ContainsFunc,ContainsFunc2Any,Any2,All,All2First,First2,FirstFunc,FirstFunc2Last,Last2,LastFunc,LastFunc2Position,Position2Compare,Compare2,CompareFunc,CompareFunc2Equal,Equal2,EqualFunc,EqualFunc2Max,MaxFunc,Min,MinFuncMinMax,MinMaxFuncIsSorted,IsSortedFunc
The stream subpackage exposes chainable function types:
stream.Seq[E]stream.Seq2[K, V]stream.Of,stream.Of2— wrap a bare iterator function (func(yield func(E) bool)/func(yield func(K, V) bool), the same underlying type asiter.Seq/iter.Seq2)stream.FromFunc,stream.FromFunc2stream.Iterate,stream.Iterate2stream.FromSlice— wrap a slice (~[]E) as aSeq[E]stream.FromMap— wrap a map (~map[K]V) as aSeq2[K, V]Iterto get back the underlyingiter.Seqoriter.Seq2
Available without Go 1.27 method-level generics:
Seq:Filter,Inspect,Take,Skip,TakeWhile,SkipWhile,StepBy,Chain,EnumerateSeq:ForEach,TryForEach,Reduce,TryReduceSeq:Size,SizeFunc,Any,All,First,Last,FirstFunc,LastFunc,Position,NthSeq:IsSortedFunc,MaxFunc,MinFunc,MinMaxFunc,ContainsFuncSeq2:Filter,Keys,Values,Swap,Inspect,Take,Skip,TakeWhile,SkipWhile,StepBy,ChainSeq2:ForEach,TryForEach,Reduce,TryReduceSeq2:Size,SizeFunc,Any,All,First,Last,FirstFunc,LastFunc,Position,NthSeq2:ContainsFunc
Available when building with Go 1.27 or newer:
Seq:Map,MapWhile,FilterMap,Split,Zip,ZipWith,Fold,TryFold,Scan,Collect,FindMap,CompareFunc,EqualFuncSeq2:Map,MapWhile,FilterMap,Join,Fold,TryFold,Collect,FindMap,CompareFunc,EqualFunc
⚠️ Experimental: thecollectorAPI is not yet stable and may change incompatibly or be removed in a future version.
The collector subpackage provides reusable terminal operations that
materialize an iter.Seq / iter.Seq2 into a container or aggregated value.
A Collector[E, R] is a named, reusable function value.
s := xiter.Range1(10)
got := collector.Collect(s, collector.ToSlice[int]())
// got == []int{0, 1, 2, ..., 9}Core types:
Collector[E, R],Collector2[K, V, R]Collect,Collect2
Collectors for iter.Seq[E]:
ToSlice,ToSortedSlice,ToSortedSliceFunc,ToSortedStableSliceFuncToSetDistinct,DistinctByKey(order-preserving global dedup)JoiningGroupingByPartitioningBy(returnsPartition[E]{Pass, Fail})Chunk(returnsiter.Seq[[]E])
Collectors for iter.Seq2[K, V]:
ToMapToKeys,ToValues
- Compose transformations as pipelines for readability.
- Delay materialization (e.g. slice/map conversion) whenever possible.
Repeatproduces an infinite sequence — always pair it with a limiting operator such asTake.Iterateis self-terminating: itsnextcallback returns(value, ok)and stops whenokis false.- Use
*_Funcvariants for custom comparison and matching logic. - Use
Inspectfor debugging or side effects in the middle of a lazy pipeline. - Use
Try*terminal operations when callbacks can fail and should stop early. - Honor the
yieldreturn value in custom sources: stop callingyieldas soon as it returnsfalse, otherwise Go 1.23+ range-over-func will panic.
go test ./...- Add more examples and benchmarks
- Continue improving
streamAPI coverage and documentation
Apache-2.0. See LICENSE for details.