Fun with Go Iterators
Go version 1.23 introduced iterator support, prompting the author to create a wrapper for easier method chaining. This abstraction enhances readability for operations like reverse, map, and filter.
Read original articleGo version 1.23 introduced iterator support, allowing developers to loop over various data structures like maps, slices, and arrays. However, the author finds the iterator creation process cumbersome compared to JavaScript's chaining capabilities. To address this, the author created a wrapper around the iter and slices packages, enabling a more streamlined and readable syntax for operations like reverse, map, filter, and forEach. The implementation includes an Iterator struct that simplifies the chaining of these operations. The author provides examples demonstrating the use of this abstraction, showcasing its effectiveness in manipulating collections of integers, runes, and structs. The tests confirm that the new iterator methods work as intended, producing expected results. The author acknowledges the Go team's design choices while advocating for a more user-friendly approach to iterators, similar to JavaScript's functional style.
- Go 1.23 introduced iterator support for various data structures.
- The author created a wrapper to simplify iterator usage and enable method chaining.
- The new abstraction allows for operations like reverse, map, filter, and forEach in a more readable format.
- Examples demonstrate the effectiveness of the new iterator methods with different data types.
- The author recognizes the Go team's design rationale while promoting a more user-friendly iterator approach.
Related
Know Go: Iterators in Go
Iterators in Go yield results one at a time, enhancing efficiency. They are detailed in function signatures, error handling, composition, and library impact. Compared to channels, iterators offer simplicity and efficiency.
Go Range Iterators Demystified
The Go 1.23 release introduces range iterators for custom collection types, offering flexibility for iteration beyond maps and slices. These iterators support various loop forms and enable powerful iteration scenarios.
First impressions of Go 1.23's range-over-func feature
The author shares positive experiences with Go 1.23's range-over-func feature, initially skeptical but finding it easy to use. Successful adaptation in their project Kivik disproved initial concerns, highlighting benefits for codebase improvement.
Go 1.23: Interactive release notes
Go 1.23 introduces interactive release notes with enhanced sequence iteration using iterators like Seq and Seq2. It formalizes iterator types, improves time.Timer behavior, and offers memory optimization tools.
Go 1.23
Go 1.23 has been released with enhancements to the toolchain, runtime, and libraries, including the "range-over-func" feature, opt-in telemetry, and updates to the standard library for improved performance.
That said, I noticed something odd here. In order for a module like this to really shine, I think all these operations need to be functionally pure. Right now, some of these mutate the iterator's `iter` method mid-stream, which is about as side-effect-ful as you can get.
```
func (i Iterator[V]) Map(f func(V) V) Iterator[V] {
cpy := i.iter
i.iter = func(yield func(V) bool) {
for v := range cpy {
v = f(v)
if !yield(v) {
return
}
}
}
return i
}
```Unless I'm misreading that, `i.iter` has new behavior after this call. A better way would be to return a new _iterator_ with the custom iter behavior instead.
``` func (i Iterator[V]) Map(f func(V) V) Iterator[V] {
// create a fresh iterator around a custom closure (NewIterator() is hypothetical in this case)
return NewIterator(func(yield func(V) bool) {
for v := range i.iter {
v = f(v)
if !yield(v) {
return
}
}
})
}
``` a := []int{1,2,3,4}
it := slices.All(a)
it = slices.Reverse(it)
it = slices.Map(it)
it = slices.Filter(it, func(i int) bool { return i % 2 == 0 })
slices.ForEach(it, func(i int) { fmt.Println(i) })
I don't judge the Go enjoyers, but I prefer writing TypeScript to Go which says it all.Type-inferred arrow lambda for function arguments would go such a long way in making this code nicer... And not make compilation slower at all.
it = slices.Filter(it, i => i % 2 == 0)
slices.ForEach(it, i => fmt.Println(i))
You are not supposed to chain them. This addiction to try and chain everything everywhere all the time is so freaking weird and has been for a very long time.
Not only you are completely losing grasp on what is going on and write code prone to errors, but you are making it unreadable for other people that will be maintaining or just reading your code who will come long after you are gone from the company or abandon your library.
This is where Go's simplicity approach and splitting each action into its own for loop or block of code is a godsend for maintainability.
(† Are iterators even expected/required to be reusable? If they are reusable, are they expected to be stable?)
Because it’s not JavaScript, and that is a good thing.
It was updated to 1.23, so it is as idiomatic as I can get. And yes it has a map method between two types. Just a single simple trick used.
https://github.com/picosh/pubsub/blob/main/pubsub.go#L18
We have seen in other languages like JS and python the power of iterators and we are happy to see it in Go
def chain( Accumulant, *Functions_list ):
for f in Functions_list: Accumulant = f( Accumulant )
return Accumulant
https://sr.ht/~tpapastylianou/chain-ops-python/2. Implicitly chain everything all the time!
In Factor, you might do it as:
reverse [ sq ] [ even? ] map-filter [ . ] each
Or with a little less optimizing: reverse [ sq ] map [ even? ] filter [ . ] each
The least obvious thing is that the period is the pretty-print function.Related
Know Go: Iterators in Go
Iterators in Go yield results one at a time, enhancing efficiency. They are detailed in function signatures, error handling, composition, and library impact. Compared to channels, iterators offer simplicity and efficiency.
Go Range Iterators Demystified
The Go 1.23 release introduces range iterators for custom collection types, offering flexibility for iteration beyond maps and slices. These iterators support various loop forms and enable powerful iteration scenarios.
First impressions of Go 1.23's range-over-func feature
The author shares positive experiences with Go 1.23's range-over-func feature, initially skeptical but finding it easy to use. Successful adaptation in their project Kivik disproved initial concerns, highlighting benefits for codebase improvement.
Go 1.23: Interactive release notes
Go 1.23 introduces interactive release notes with enhanced sequence iteration using iterators like Seq and Seq2. It formalizes iterator types, improves time.Timer behavior, and offers memory optimization tools.
Go 1.23
Go 1.23 has been released with enhancements to the toolchain, runtime, and libraries, including the "range-over-func" feature, opt-in telemetry, and updates to the standard library for improved performance.