Compare with other libraries.
How fp-go stacks up against samber/lo, go-functional, and mo — features, code, and the right tool for the job.
At-a-glance matrix.
| Feature | fp-go | Others |
|---|---|---|
Monadic types | Full (Result, Either, Option, IO, …)samber/lo: none · go-functional: limited · mo: some. | |
Type safety | Excellent (generics)samber/lo: uses any · go-functional: good · mo: good. | |
Error handling | Built-in (Result, Either)samber/lo: manual · go-functional: built-in · mo: built-in. | |
Lazy evaluation | IO, Lazy typessamber/lo: no · go-functional: yes · mo: no. | |
Composition | Pipe, Flow, Composesamber/lo: limited · go-functional: yes · mo: limited. | |
Documentation | Comprehensivesamber/lo: good · go-functional: limited · mo: good. | |
Learning curve | Medium (FP concepts)samber/lo: low · go-functional: medium · mo: low. | |
Performance | Good (idiomatic: excellent)samber/lo: excellent · go-functional: unknown · mo: good. | |
Production use | IBM, otherssamber/lo: wide · go-functional: unknown · mo: growing. | |
Active development | Yessamber/lo: yes · go-functional: sporadic · mo: yes. | |
Go version | v2: 1.24+, v1: 1.18+All others: 1.18+. |
fp-go vs. samber/lo.
samber/lo is a Lodash-inspired utility library focused on collection operations.
Collection operations
- samber/lo
- fp-go
import "github.com/samber/lo"
// Filter and map
result := lo.Map(
lo.Filter(numbers, func(n int, _ int) bool {
return n > 0
}),
func(n int, _ int) int {
return n * 2
},
)
// No built-in error handling
// Must handle errors manuallySimple, intuitive API.
Excellent performance.
Wide adoption.
Low learning curve.
No monadic types.
No built-in error handling.
Limited composition.
Uses any for some operations.
import (
"github.com/IBM/fp-go/v2/array"
"github.com/IBM/fp-go/v2/function"
"github.com/IBM/fp-go/v2/result"
)
// Filter and map with error handling
result := function.Pipe2(
array.Filter(func(n int) bool { return n > 0 }),
array.TraverseResult(func(n int) result.Result[int] {
if n > 100 {
return result.Err[int](errors.New("too large"))
}
return result.Ok(n * 2)
}),
)(numbers)
// Automatic error propagationFull monadic types.
Built-in error handling.
Excellent composition.
Type-safe throughout.
Steeper learning curve.
More verbose for simple operations.
Requires FP knowledge.
Error handling
- samber/lo
- fp-go
// Manual error handling required
results := make([]Result, 0)
for _, item := range items {
result, err := process(item)
if err != nil {
return nil, err
}
results = append(results, result)
}// Automatic error handling results := array.TraverseResult(process)(items) // Returns Result[[]Result] - single error handling point
Simple collection operations.
No complex error handling needed.
Team unfamiliar with FP.
Performance is critical.
Complex error handling.
Need monadic composition.
Building data pipelines.
Type safety is paramount.
fp-go vs. go-functional.
go-functional is a functional library built around iterators and monadic types.
- go-functional
- fp-go
import "github.com/BooleanCat/go-functional/v2/it"
// Iterator-based operations
result := it.Map(
it.Filter(
it.Lift(numbers),
func(n int) bool { return n > 0 },
),
func(n int) int { return n * 2 },
)Monadic types.
Iterator-based (lazy).
Good type safety.
Limited documentation.
Smaller ecosystem.
Less active development.
Fewer types than fp-go.
import (
"github.com/IBM/fp-go/v2/array"
"github.com/IBM/fp-go/v2/function"
)
// Array-based operations
result := function.Pipe2(
array.Filter(func(n int) bool { return n > 0 }),
array.Map(func(n int) int { return n * 2 }),
)(numbers)
// Also supports lazy iteratorsMore monadic types (Result, Either, IO, Reader, …).
Comprehensive documentation.
Active development.
Production-proven.
Larger API surface.
More concepts to learn.
fp-go vs. mo.
mo is a lightweight FP library focused on Option, Result, and Either.
- mo
- fp-go
import "github.com/samber/mo" // Option type opt := mo.Some(42) value := opt.OrElse(0) // Result type result := mo.Ok[int](42) value = result.OrElse(0)
Simple API.
Good documentation.
Easy to learn.
Good performance.
Limited types (Option, Result, Either).
No IO or Reader types.
Limited composition utilities.
No lazy evaluation.
import (
"github.com/IBM/fp-go/v2/option"
"github.com/IBM/fp-go/v2/result"
)
// Option type
opt := option.Some(42)
value := option.GetOrElse(func() int { return 0 })(opt)
// Result type
res := result.Ok(42)
value = result.GetOrElse(func() int { return 0 })(res)
// Plus: IO, Reader, State, and moreFull FP toolkit (20+ types).
IO and Reader for effects.
Lazy evaluation support.
Comprehensive composition.
More complex API.
Steeper learning curve.
Overkill for simple use cases.
Feature deep dive.
Error handling
| Library | Approach | Composition |
|---|---|---|
fp-go | Result/Either typesType safety: excellent. Composition: excellent. | |
samber/lo | Manual (Go style)Type safety: good. Composition: none. | |
go-functional | Result typeType safety: good. Composition: good. | |
mo | Result/Either typesType safety: good. Composition: limited. |
Collection operations
| Library | Operations | Notes |
|---|---|---|
fp-go | 30+ operationsGood performance (idiomatic: excellent). Type safety: excellent. | |
samber/lo | 100+ operationsExcellent performance. Uses any. | |
go-functional | 20+ operationsPerformance unknown. Type safety: good. | |
mo | 10+ operationsGood performance. Type safety: good. |
Monadic types
| Library | Types available | |
|---|---|---|
fp-go | Result, Either, Option, IO, IOResult, IOEither, IOOption, Reader, ReaderIO, ReaderIOResult, State, Lazy, Identity, Effect, and more | |
samber/lo | None | |
go-functional | Option, Result, Iterator | |
mo | Option, Result, Either, Future, Task |
Composition utilities
| Library | Available | Notes |
|---|---|---|
fp-go | Pipe, Flow, Compose, customMany composition combinators. | |
samber/lo | LimitedNo first-class Pipe/Flow/Compose. | |
go-functional | Pipe, FlowSome. | |
mo | LimitedNo first-class Pipe/Flow/Compose. |
Use-case recommendations.
| Use case | Winner | Snippet |
|---|---|---|
Simple collection ops | samber/lolo.Map(lo.Filter(items, predicate), transform) | |
Complex error handling | fp-gofunction.Pipe4(step1(), result.Chain(step2), result.Chain(step3), result.Chain(step4)) | |
Lightweight Option/Result | momo.Some(value) · mo.Ok[int](42) | |
Full FP application | fp-goreaderioresult.Ask + Chain pipelines | |
Iterator-based processing | go-functional or fp-goit.Map(it.Filter(it.Lift(data), pred), transform) |
Migration paths.
From samber/lo to fp-go
- Before (lo)
- After (fp-go)
import "github.com/samber/lo"
result := lo.Map(
lo.Filter(numbers, func(n int, _ int) bool {
return n > 0
}),
func(n int, _ int) int {
return n * 2
},
)
import (
"github.com/IBM/fp-go/v2/array"
"github.com/IBM/fp-go/v2/function"
)
result := function.Pipe2(
array.Filter(func(n int) bool { return n > 0 }),
array.Map(func(n int) int { return n * 2 }),
)(numbers)From mo to fp-go
- Before (mo)
- After (fp-go)
import "github.com/samber/mo" opt := mo.Some(42) value := opt.OrElse(0)
import "github.com/IBM/fp-go/v2/option"
opt := option.Some(42)
value := option.GetOrElse(func() int { return 0 })(opt)Performance comparison.
| Variant | Speed | ns/op | B/op | Δ |
|---|---|---|---|---|
| stdlib | 1,050 | — | baseline | |
| samber/lo | 1,100 | — | +5% | |
| fp-go (idiomatic) | 1,080 | — | +3% | |
| fp-go (standard) | 1,200 | — | +14% |
- samber/lo: Excellent performance, close to stdlib
- fp-go standard: Small overhead for type safety
- fp-go idiomatic: Near-native performance
- All are fast enough for most use cases
Decision matrix.
Full FP toolkit.
Complex error handling.
IO and effect management.
Reader pattern (dependency injection).
Comprehensive documentation.
Production support.
Simple collection operations.
Minimal learning curve.
Maximum performance.
Wide community adoption.
Quick wins.
Lightweight Option/Result.
Simple API.
Minimal dependencies.
Quick adoption.
Iterator-based operations.
Lazy evaluation.
Simpler than fp-go.
Basic monadic types.
Can you use multiple libraries?
import (
"github.com/IBM/fp-go/v2/result"
"github.com/samber/lo"
)
// Use lo for simple operations
filtered := lo.Filter(items, predicate)
// Use fp-go for complex error handling
result := result.TraverseArray(func(item Item) result.Result[Processed] {
return processWithErrors(item)
})(filtered)- Use samber/lo for simple collection operations.
- Use fp-go for error handling and composition.
- Use mo for lightweight Option/Result in simple modules.
- Choose one as your primary library.
Summary
| Aspect | Best choice | |
|---|---|---|
Simplicity | samber/lo or mo | |
Type safety | fp-go | |
Performance | samber/lo or fp-go idiomatic | |
Error handling | fp-go | |
Learning curve | samber/lo | |
Composition | fp-go | |
Documentation | fp-go or samber/lo | |
Production use | fp-go or samber/lo | |
Full FP | fp-go |