Files
lo/docs/static/llms.txt
T

515 lines
20 KiB
Plaintext

# Lo - A Go library for functional programming
Lo is a comprehensive, production-ready Lodash-style Go library built on Go 1.18+ generics that provides hundreds of utility functions for working with slices, maps, strings, channels, functions, and more. It aims to make Go code more expressive, readable, and productive by offering a rich collection of functional programming helpers inspired by libraries like Lodash in JavaScript.
## Origins and Purpose
Born from the need to fill gaps in Go's standard library after the introduction of generics in Go 1.18, Lo addresses common pain points that Go developers face daily. While Go's standard library gained some generic helpers in `slices` and `maps` packages, Lo goes far beyond with 300+ carefully crafted utilities that make functional programming patterns natural and idiomatic in Go.
The library emerged from real-world production needs and has been battle-tested in numerous high-traffic applications, microservices, and enterprise systems. It's not just a collection of utility functions, but a thoughtful toolkit that enables developers to write more declarative, maintainable, and bug-free code.
## Key Features
- **Generic-based**: Fully leverages Go 1.18+ generics for compile-time type safety
- **Zero dependencies**: No external dependencies outside the Go standard library
- **Comprehensive**: 300+ helpers covering slices, maps, strings, channels, functions, and more
- **Functional programming**: Brings functional programming paradigms to Go naturally
- **Production-ready**: Battle-tested in high-traffic applications worldwide
- **Well-tested**: 95%+ test coverage with comprehensive examples
- **SemVer compliant**: Follows strict semantic versioning (v1.x.x stable)
- **Performance-focused**: Minimal runtime overhead with optimized implementations
- **Memory efficient**: Many helpers avoid allocations and use zero-copy patterns
- **Composable**: Designed to work together in method chains and complex pipelines
## Sub-packages
The main package is complemented by several specialized sub-packages:
- **lo**: Core package with 300+ helpers for everyday operations
- **lo/parallel**: Parallel processing helpers for concurrent operations
- **lo/mutable**: In-place mutation helpers for memory-efficient operations
- **lo/it**: Iterator helpers for lazy evaluation and streaming
## Design Philosophy
Lo is built on a foundation of pragmatic engineering principles that balance power, safety, and performance:
1. **Type Safety First**: All helpers leverage Go's generics for compile-time type checking, eliminating runtime type assertions and reducing bugs
2. **Go Idiomatic**: While inspired by functional programming, helpers feel natural in Go code and follow established conventions
3. **Performance Conscious**: Every helper is benchmarked and optimized for minimal overhead, with many using zero-copy patterns
4. **Explicit Clarity**: Helper names clearly communicate their purpose and behavior
5. **Composable by Design**: Functions work together seamlessly, enabling elegant data transformation pipelines
6. **Memory Efficient**: Careful attention to allocation patterns, with many helpers avoiding unnecessary heap allocations
7. **Error Handling Respect**: Helpers don't hide errors; they integrate naturally with Go's error handling patterns
8. **Practical Over Pure**: Prioritizes solving real-world problems over theoretical purity
## Root Package Helpers
### Condition
- If: Conditional statement that returns a value
- IfF: Conditional statement with function execution
- Switch: Switch statement that returns a value
- Ternary: Ternary operator
- TernaryF: Ternary operator with function execution
### Concurrency
- Async: Execute function asynchronously
- Async0-Async6: Execute functions with 0-6 return values asynchronously
- Synchronize: Synchronize goroutines
- WaitFor: Wait for condition to be true
- WaitForWithContext: Wait for condition with context
### Error Handling
- Validate: Validate condition and return error
- Must0-Must6: Must helpers with 0-6 return values
- Try0-Try6: Try helpers with 0-6 return values
- TryOr: Try with fallback value
- TryOr1-TryOr6: Try with fallback values
- TryWithErrorValue: Try with error value
- TryCatch: Try-catch block
- TryCatchWithErrorValue: Try-catch with error value
- ErrorsAs: Convert errors
- Assert: Assert condition
- Assertf: Assert condition with formatting
### Find
- Find: Find element in collection
- FindOrElse: Find element or return fallback
- FindIndexOf: Find element and its index
- FindLastIndexOf: Find last occurrence and its index
- FindKey: Find key in map by value
- FindKeyBy: Find key by predicate
- FindUniques: Find unique elements
- FindUniquesBy: Find unique elements by predicate
- FindDuplicates: Find duplicate elements
- FindDuplicatesBy: Find duplicates by predicate
- First: Get first element
- FirstOr: Get first element or fallback
- FirstOrEmpty: Get first element or empty value
- Last: Get last element
- LastOr: Get last element or fallback
- LastOrEmpty: Get last element or empty value
- Nth: Get nth element
- NthOr: Get nth element or fallback
- NthOrEmpty: Get nth element or empty value
- Sample: Sample random element
- SampleBy: Sample by predicate
- Samples: Sample multiple elements
- SamplesBy: Sample multiple by predicate
- Min: Find minimum value
- MinBy: Find minimum by predicate
- MinIndex: Find minimum index
- MinIndexBy: Find minimum index by predicate
- Max: Find maximum value
- MaxBy: Find maximum by predicate
- MaxIndex: Find maximum index
- MaxIndexBy: Find maximum index by predicate
- Earliest: Find earliest time
- EarliestBy: Find earliest by predicate
- Latest: Find latest time
- LatestBy: Find latest by predicate
### Function
- Partial: Create partial function
- Partial1-Partial5: Create partial functions with 1-5 arguments
- NewDebounce: Create debounced function
- NewDebounceBy: Create debounced function by key
- NewThrottle: Create throttled function
- NewThrottleBy: Create throttled function by key
- NewThrottleWithCount: Create throttled function with count
- NewThrottleByWithCount: Create throttled function by key with count
- NewTransaction: Create transaction
- Duration: Measure execution duration
- Duration0-Duration10: Measure duration with 0-10 return values
### Intersect
- Intersect: Find intersection of collections
- Difference: Find difference between collections
- Union: Find union of collections
- ElementsMatch: Check if elements match
- ElementsMatchBy: Check if elements match by predicate
- Contains: Check if collection contains element
- ContainsBy: Check if collection contains by predicate
- HasPrefix: Check if collection has prefix
- HasSuffix: Check if collection has suffix
- Some: Check if collection contains some elements
- SomeBy: Check if collection contains some by predicate
- Every: Check if collection contains all elements
- EveryBy: Check if collection contains all by predicate
- None: Check if collection contains none
- NoneBy: Check if collection contains none by predicate
- IndexOf: Find index of element
- LastIndexOf: Find last index of element
### Map
- Map: Apply function to collection
- MapValues: Apply function to map values
- MapKeys: Apply function to map keys
- MapEntries: Apply function to map entries
- MapToSlice: Convert map to slice
- FilterMapToSlice: Filter and map to slice
- FlatMap: Apply function and flatten
- FilterMap: Filter and map
- RejectMap: Reject and map
### Math
- Range: Generate range of numbers
- RangeFrom: Generate range from start
- RangeWithSteps: Generate range with steps
- Clamp: Clamp value between min and max
- Sum: Sum numbers
- SumBy: Sum by predicate
- Product: Product of numbers
- ProductBy: Product by predicate
- Mean: Mean of numbers
- MeanBy: Mean by predicate
- Mode: Mode of numbers
### Retry
- Attempt: Attempt operation with retries
- AttemptWhile: Attempt while condition
- AttemptWithDelay: Attempt with delay
- AttemptWhileWithDelay: Attempt while with delay
### Slice
- Chunk: Split slice into chunks
- ChunkString: Split string into chunks
- Drop: Drop first n elements
- DropRight: Drop last n elements
- DropWhile: Drop while condition
- DropRightWhile: Drop right while condition
- DropByIndex: Drop elements by index
- Filter: Filter collection
- FilterReject: Filter and reject
- Reject: Reject elements
- Compact: Compact collection
- Fill: Fill collection
- ForEach: Apply function to each element
- ForEachWhile: Apply function while condition
- GroupBy: Group elements by key
- GroupByMap: Group elements by key to map
- PartitionBy: Partition elements by predicate
- Count: Count elements
- CountBy: Count by predicate
- CountValues: Count values
- CountValuesBy: Count values by predicate
- Reduce: Reduce collection
- ReduceRight: Reduce from right
- Repeat: Repeat element
- RepeatBy: Repeat by predicate
- Times: Call function n times
- Replace: Replace elements
- ReplaceAll: Replace all elements
- Reverse: Reverse collection
- Slice: Slice collection
- Splice: Splice collection
- Subset: Get subset of collection
- Shuffle: Shuffle collection
- Uniq: Get unique elements
- UniqBy: Get unique by predicate
- Without: Remove elements
- WithoutBy: Remove elements by predicate
- WithoutNth: Remove nth element
- WithoutEmpty: Remove empty elements
- IsSorted: Check if collection is sorted
- IsSortedByKey: Check if sorted by key
- Interleave: Interleave collections
### String
- Substring: Get substring
- CamelCase: Convert to camel case
- PascalCase: Convert to pascal case
- KebabCase: Convert to kebab case
- SnakeCase: Convert to snake case
- Capitalize: Capitalize string
- Words: Split string into words
- Ellipsis: Truncate string with ellipsis
- Elipse: Alias for Ellipsis
- RandomString: Generate random string
- RuneLength: Get rune length
- Trim: Trim string
- TrimLeft: Trim left
- TrimRight: Trim right
- TrimPrefix: Trim prefix
- TrimSuffix: Trim suffix
- Cut: Cut string at separator
- CutPrefix: Cut prefix from string
- CutSuffix: Cut suffix from string
### Time
- Duration: Measure duration
- Duration0-Duration10: Measure duration with return values
- WaitFor: Wait for condition
- WaitForWithContext: Wait for condition with context
- Earliest: Find earliest time
- EarliestBy: Find earliest by predicate
- Latest: Find latest time
- LatestBy: Find latest by predicate
### Tuple
- T2-T9: Create tuples with 2-9 elements
- Unzip2-Unzip9: Unzip tuples with 2-9 elements
- UnzipBy2-UnzipBy9: Unzip by predicate with 2-9 elements
- Zip2-Zip9: Zip collections with 2-9 elements
- ZipBy2-ZipBy9: Zip by predicate with 2-9 elements
- CrossJoin2-CrossJoin9: Cross join with 2-9 collections
- CrossJoinBy2-CrossJoinBy9: Cross join by predicate with 2-9 collections
### Type
- KeyBy: Key collection by field
- Keys: Get keys from map
- Values: Get values from map
- Entries: Get entries from map
- FromEntries: Create map from entries
- ToPairs: Convert map to pairs
- FromPairs: Create map from pairs
- HasKey: Check if map has key
- PickBy: Pick by predicate
- PickByKeys: Pick by keys
- PickByValues: Pick by values
- OmitBy: Omit by predicate
- OmitByKeys: Omit by keys
- OmitByValues: Omit by values
- Invert: Invert map
- Assign: Assign maps
- ChunkEntries: Split map entries into chunks
- Associate: Create map from collection
- SliceToMap: Convert slice to map
- FilterSliceToMap: Filter and convert slice to map
- ValueOr: Get value or fallback
- Keyify: Create set from collection
- Empty: Get empty value
- IsEmpty: Check if value is empty
- IsNotEmpty: Check if value is not empty
- Coalesce: Return first non-empty value
- CoalesceOrEmpty: Return first non-empty or empty
- CoalesceSlice: Return first non-empty slice
- CoalesceSliceOrEmpty: Return first non-empty slice or empty
- CoalesceMap: Return first non-empty map
- CoalesceMapOrEmpty: Return first non-empty map or empty
- ToPtr: Convert to pointer
- FromPtr: Convert from pointer
- FromPtrOr: Convert from pointer or fallback
- ToSlicePtr: Convert slice to pointer slice
- FromSlicePtr: Convert pointer slice to slice
- FromSlicePtrOr: Convert pointer slice or fallback
- ToAnySlice: Convert to any slice
- FromAnySlice: Convert from any slice
- IsNil: Check if nil
- IsNotNil: Check if not nil
- Nil: Return nil pointer
- EmptyableToPtr: Convert emptyable to pointer
- Validate: Validate condition
### Channel
- ChannelToSlice: Convert channel to slice
- SliceToChannel: Convert slice to channel
- Batch: Batch channel values
- BatchWithTimeout: Batch with timeout
- Buffer: Buffer channel values
- BufferWithTimeout: Buffer with timeout
- BufferWithContext: Buffer with context
- ChannelMerge: Merge channels
- ChannelDispatcher: Dispatch to multiple channels
- FanIn: Fan in channels
- FanOut: Fan out channels
- Generator: Generate channel values
- DispatchingStrategyFirst: First dispatch strategy
- DispatchingStrategyLeast: Least dispatch strategy
- DispatchingStrategyMost: Most dispatch strategy
- DispatchingStrategyRandom: Random dispatch strategy
- DispatchingStrategyRoundRobin: Round robin dispatch strategy
- DispatchingStrategyWeightedRandom: Weighted random dispatch strategy
## Additional Resources
- **Documentation**: Comprehensive documentation with examples for every helper
- **GoDoc**: https://pkg.go.dev/github.com/samber/lo
- **GitHub**: https://github.com/samber/lo
- **Playground**: Every helper includes a Go Playground example for quick testing
- **Community**: Active community with contributions from hundreds of developers
## Usage Examples
### Basic Collection Operations
```go
import "github.com/samber/lo"
// Remove duplicates from a slice
names := lo.Uniq([]string{"Samuel", "John", "Samuel"})
// []string{"Samuel", "John"}
// Filter and transform in one operation
numbers := lo.FilterMap([]int{1, 2, 3, 4, 5}, func(item int, index int) (string, bool) {
if item%2 == 0 {
return fmt.Sprintf("even-%d", item), true
}
return "", false
})
// []string{"even-2", "even-4"}
```
### Advanced Data Processing
```go
// Complex data transformation pipeline
users := []User{{ID: 1, Name: "Alice", Active: true}, {ID: 2, Name: "Bob", Active: false}}
// Group active users by name length, then extract IDs
activeUserIDs := lo.Pipe(
users,
lo.Filter(func(u User) bool { return u.Active }),
lo.GroupBy(func(u User) int { return len(u.Name) }),
lo.MapValues(func(users []User) []int {
return lo.Map(users, func(u User) int { return u.ID })
}),
)
// map[int][]int{5: []int{1}}
// Safe nested data access
config := map[string]any{
"database": map[string]any{
"host": "localhost",
"port": 5432,
},
}
host := lo.ValueOr(lo.CoalesceMap(
lo.MapValues(config, func(v any) map[string]any {
if m, ok := v.(map[string]any); ok {
return m
}
return nil
}),
), "localhost")
```
### Concurrency and Async Operations
```go
// Parallel processing of large datasets
results := lop.Map(items, func(item Item) Result {
return processItem(item) // Executed in parallel
})
// Debounce API calls to avoid rate limiting
debouncedSearch := lo.NewDebounce(300*time.Millisecond, func() {
results := searchAPI(query)
updateUI(results)
})
// Safe error handling with fallbacks
result, ok := lo.TryOr(func() (int, error) {
return dangerousOperation()
}, 0)
```
### Functional Control Flow
```go
// Elegant conditional logic
message := lo.If(user != nil, "Welcome back!").
ElseIfF(isGuest, func() string { return "Welcome guest!" }).
Else("Please login")
// Retry logic with exponential backoff
result, err := lo.AttemptWithDelay(3, time.Second, func(i int, d time.Duration) error {
return callExternalAPI(context.Background())
})
// Transaction management
tx := lo.NewTransaction[Order]()
defer tx.Rollback()
order, err := tx.Run(func() (Order, error) {
inventory := checkInventory(items)
if !inventory.Available {
return Order{}, errors.New("insufficient inventory")
}
return createOrder(user, items)
})
```
## Performance Considerations
Lo is engineered for performance-critical applications with several optimization strategies:
### Memory Efficiency
- **Zero-copy patterns**: Many helpers avoid unnecessary allocations by working with existing data
- **Slice reuse**: Mutable variants (`lo/mutable`) enable in-place operations for memory-constrained environments
- **Lazy evaluation**: Iterator patterns in `lo/iter` support streaming of large datasets
- **Pre-allocation**: Collection sizes are pre-calculated when possible to minimize reallocations
### Performance Characteristics
- **Compile-time optimization**: Generic types enable compile-time specialization and optimization
- **Minimal overhead**: Most helpers add zero or single-digit nanosecond overhead
- **Cache-friendly**: Linear memory access patterns for better CPU cache utilization
- **Branch prediction**: Hot paths are optimized for common case execution
### Benchmarks and Optimization
Every helper includes comprehensive benchmarks covering:
- Different input sizes (small, medium, large)
- Various data types (int, string, structs)
- Comparison with standard library alternatives
- Memory allocation tracking
```go
// Example benchmark results for lo.Uniq vs manual implementation
BenchmarkLoUniq-8 1000000 1200 ns/op 800 B/op 4 allocs/op
BenchmarkManualUniq-8 5000000 240 ns/op 0 B/op 0 allocs/op (when in-place)
```
### Production Optimization Tips
1. **Use `lo/mutable`** for large datasets when memory is a concern
2. **Leverage `lo/parallel`** for CPU-bound operations on large collections
3. **Choose appropriate helpers** - some have specialized variants for specific use cases
4. **Profile your code** - use Go's built-in profiling tools to identify bottlenecks
5. **Consider streaming** - use iterators for very large datasets that don't fit in memory
## Ecosystem and Community
### Related Projects
Lo is part of a growing ecosystem of Go libraries that enhance developer productivity:
- **[samber/do](https://github.com/samber/do)**: A dependency injection toolkit based on Go 1.18+ Generics
- **[samber/mo](https://github.com/samber/mo)**: Monads based on Go 1.18+ Generics (Option, Result, Either...)
- **[samber/lo-logrus](https://github.com/samber/lo-logrus)**: Lo helpers for logrus structured logging
### Community and Contributions
- **Active development**: Regular updates and improvements from maintainers
- **Community contributions**: Hundreds of contributors from around the world
- **Corporate adoption**: Used by major companies including startups, fintech, and enterprise organizations
- **Discussions**: Active GitHub discussions for feature requests and support
### Learning Resources
- **Documentation**: Comprehensive docs with examples for every helper
- **GoDoc**: https://pkg.go.dev/github.com/samber/lo
- **GitHub Discussions**: Community support and discussions
- **Examples**: Real-world usage examples in the repository
## Migration and Adoption
### From Standard Library
Migrating from standard library `slices` and `maps` packages is straightforward:
- Lo provides similar APIs with additional features
- Many helpers drop-in replace standard library functions
- Enhanced error handling and edge case coverage
### From Other Libraries
If you're coming from other utility libraries:
- Lo emphasizes Go idioms over direct ports from other languages
- Type safety is prioritized over dynamic patterns
- Performance is a key consideration in all implementations
## License
MIT License - see LICENSE file for details
## Acknowledgments
Lo stands on the shoulders of giants:
- Inspired by Lodash, Underscore.js, and functional programming concepts
- Built upon Go's excellent generics implementation
- Community-driven with contributions from hundreds of developers
- Production-tested across diverse use cases and industries