https://github.com/jasonbot/chains
Fun tricks for stream-style programming: the inevitable, sad but logical conclusion of iterators and generics in Go
https://github.com/jasonbot/chains
functional-programming golang iterator itertools
Last synced: 2 months ago
JSON representation
Fun tricks for stream-style programming: the inevitable, sad but logical conclusion of iterators and generics in Go
- Host: GitHub
- URL: https://github.com/jasonbot/chains
- Owner: jasonbot
- License: bsd-3-clause
- Created: 2024-11-24T02:59:23.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-09-18T18:11:07.000Z (6 months ago)
- Last Synced: 2025-09-18T21:10:49.304Z (6 months ago)
- Topics: functional-programming, golang, iterator, itertools
- Language: Go
- Homepage: https://pkg.go.dev/jasonscheirer.com/chains
- Size: 113 KB
- Stars: 5
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Chained Iterators in Go
> [!NOTE]
> I wrote [a blog post on this library](https://www.jasonscheirer.com/weblog/chains/), which is a nice medium-form introduction to the lib and its motivators/usage.
I haven't done much in Go lately, and I definitely haven't played with generics or iterators yet. Why not do both?
So now you can do this:
```go
myArray := []int{1, 2, 3}
returnArray := ChainFromSlice(
[]int{8, 10, 145, 3},
).Map(
func(i int) int {
return i * 3
},
).Filter(
func(i int) bool {
return i%2 == 0
},
).Slice()
// returnArray == []int{24, 30}
```
This library is meant to fill a void in some of the niceness I get in Python and Ruby -- you'll note there is a whole subset of [Python's `itertools` library](https://docs.python.org/3/library/itertools.html) here.
## Examples
Interesting examples live in [`cookbook_test`](cookbook_test.go).
## Warts
The Go templating system is a little limited, so you can't do something like this:
```go
arrayOfStrings := ChainFromSlice(
[]int{8, 10, 145, 3},
).Map(
// Compiler can't infer you're going from Chain[int] to Chain[string]
func(i int) string {
return fmt.Sprintf("%v", i)
},
)
```
The generic system does not allow for templated methods, so chaining methods and expecting to go from `Chain[T]` to `Chain[V]` isn't possible.
You need to give the templating system a hint with a junction, telling it there's 2 types involved:
```go
mapFunc := func(i int) string { return fmt.Sprintf("%v", i) }
array := []int{1, 2, 3, 4}
// Converting type in .Map(), so the generic has to be aware of both types
returnArray := ChainJunction[int, string](ChainFromSlice(
array,
).Filter(
func(i int) bool {
return i%2 == 0
},
)).Map(
mapFunc,
).Slice()
// secondreturnArrayArray == []string{"2", "4"}
```