Generic utility functions for Go - functional programming made easy
Henry is a collection of generic utility functions for Go 1.18+, providing functional programming primitives for slices, maps, and channels. It offers both standalone functions and fluent APIs to make your code more expressive and maintainable.
- Zero-allocation overhead - Type aliases where possible, no wrapper structs
- Generic-first design - Leverages Go 1.18+ generics for type safety
- Consistent API - Similar patterns across all packages
- Well-documented - Every function has clear examples
- Production-ready - Comprehensive test coverage (95%+)
go get github.com/modfin/henry/...import (
"github.com/modfin/henry/slicez"
"github.com/modfin/henry/mapz"
"github.com/modfin/henry/chanz"
)
// Transform data
numbers := []int{1, 2, 3, 4, 5}
doubled := slicez.Map(numbers, func(n int) int { return n * 2 })
// doubled = []int{2, 4, 6, 8, 10}
// Filter collections
evens := slicez.Filter(numbers, func(n int) bool { return n%2 == 0 })
// evens = []int{2, 4}
// Work with maps
scores := map[string]int{"Alice": 85, "Bob": 92}
names := mapz.Keys(scores)
// names = []string{"Alice", "Bob"} (order not guaranteed)
// Process streams
input := chanz.Generate(1, 2, 3, 4, 5)
doubledCh := chanz.Map(input, func(n int) int { return n * 2 })
result := chanz.Collect(doubledCh)
// result = []int{2, 4, 6, 8, 10}| Package | Description | Key Features |
|---|---|---|
| slicez | Slice operations | Map, Filter, Reduce, Sort, Set operations |
| mapz | Map operations | Keys/Values, Filter, Merge, Transform |
| chanz | Channel pipelines | FanIn/FanOut, Map, Filter, Done signals |
| setz | Set data structure | Union, Intersection, Difference |
| pipez | Fluent API | Method chaining for slice operations |
| compare | Comparison utilities | Less, Greater, Between, Clamp |
| mon | Monadic types | Result, Option types for error handling |
// Traditional imperative approach
var evenSquares []int
for _, n := range numbers {
if n%2 == 0 {
evenSquares = append(evenSquares, n*n)
}
}
// Functional approach with Henry
evenSquares := pipez.Of(numbers).
Filter(func(n int) bool { return n%2 == 0 }).
Map(func(n int) int { return n * n }).
Slice()// Using the Result monad from mon package
import "github.com/modfin/henry/mon"
urls := []string{"https://example.com", "https://github.com", "bad url"}
// Parse URLs safely
parsed := slicez.Map(urls, func(s string) mon.Result[*url.URL] {
u, err := url.Parse(s)
return mon.TupleToResult(u, err)
})
// Extract valid URLs and errors
validURLs, errs := mon.Partition(parsed)// Generate work items
work := chanz.Generate(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// Process in parallel (fan-out)
workers := chanz.FanOut(work, 3)
// Process each worker channel
for i, worker := range workers {
go func(id int, ch <-chan int) {
for n := range ch {
result := process(n)
fmt.Printf("Worker %d processed %d\n", id, result)
}
}(i, worker)
}Henry is designed with performance in mind:
- Pre-allocated slices - Functions like
Map,Filter, andUnionpre-calculate capacity - Zero-overhead types -
PipeandSetare type aliases, not wrapper structs - Efficient algorithms - Uses Fisher-Yates shuffle, swap-to-end sampling
- In-place operations - Some functions (clearly marked) mutate for performance
// Pre-allocation example
data := make([]int, 10000)
// Map pre-allocates result slice with same capacity
result := slicez.Map(data, transform) // No reallocations!| Task | Standard Library | Henry |
|---|---|---|
| Sort slice | sort.Slice() |
slicez.Sort() or slicez.SortBy() |
| Filter slice | Manual loop | slicez.Filter() |
| Map slice | Manual loop | slicez.Map() |
| Check contains | Manual loop | slicez.Contains() |
| Merge maps | Manual loop | mapz.Merge() |
| Channel transform | Manual goroutine | chanz.Map() |
- golang.org/x/exp - Official experimental packages
- samber/lo - Lodash-style library
- thoas/go-funk - Functional utilities
Contributions welcome! Please ensure:
- Tests pass:
go test ./... - Code is formatted:
go fmt ./... - Documentation is updated for new functions
MIT License - see LICENSE file for details.