How to Chain and Compose Iterators in Go

Chain and compose iterators in Go by defining functions that return closures accepting a yield function, then nesting calls to combine filtering and mapping logic.

Go does not have built-in iterator chaining like other languages; you compose iteration logic by passing functions or using the range over function syntax introduced in Go 1.23. To chain operations, define functions that return a closure accepting a yield function, then call the next iterator with the previous one's logic as the body.

func Map[T, U any](src []T, f func(T) U) func(func(U) bool) {
    return func(yield func(U) bool) {
        for _, v := range src {
            if !yield(f(v)) {
                return
            }
        }
    }
}

func Filter[T any](src []T, pred func(T) bool) func(func(T) bool) {
    return func(yield func(T) bool) {
        for _, v := range src {
            if pred(v) && !yield(v) {
                return
            }
        }
    }
}

// Usage: Chain Filter then Map
for v := range Filter([]int{1, 2, 3, 4}, func(x int) bool { return x%2 == 0 })(Map(func(x int) string { return fmt.Sprintf("%d", x) })) {
    fmt.Println(v)
}

Note: The example above uses a simplified syntax for clarity; in actual Go 1.23+ code, you would use for v := range Filter(...) directly if the function signature matches func(func(T) bool), or wrap the composition logic in a helper that returns the final iterator function.