Skip to main content
Version: v2.2.82 (latest)
Getting started · Section 05 / 05

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.

// Libraries compared
4
fp-go · samber/lo · go-functional · mo.
// Best for full FP
fp-go. 20+ monadic types.
// Best for simple ops
samber/lo. Low learning curve.
01

At-a-glance matrix.

Featurefp-goOthers
Monadic typesFull (Result, Either, Option, IO, …)
samber/lo: none · go-functional: limited · mo: some.
Type safetyExcellent (generics)
samber/lo: uses any · go-functional: good · mo: good.
Error handlingBuilt-in (Result, Either)
samber/lo: manual · go-functional: built-in · mo: built-in.
Lazy evaluationIO, Lazy types
samber/lo: no · go-functional: yes · mo: no.
CompositionPipe, Flow, Compose
samber/lo: limited · go-functional: yes · mo: limited.
DocumentationComprehensive
samber/lo: good · go-functional: limited · mo: good.
Learning curveMedium (FP concepts)
samber/lo: low · go-functional: medium · mo: low.
PerformanceGood (idiomatic: excellent)
samber/lo: excellent · go-functional: unknown · mo: good.
Production useIBM, others
samber/lo: wide · go-functional: unknown · mo: growing.
Active developmentYes
samber/lo: yes · go-functional: sporadic · mo: yes.
Go versionv2: 1.24+, v1: 1.18+
All others: 1.18+.
02

fp-go vs. samber/lo.

samber/lo is a Lodash-inspired utility library focused on collection operations.

Collection operations

lo.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 manually
Afterpros

Simple, intuitive API.

Excellent performance.

Wide adoption.

Low learning curve.

Beforecons

No monadic types.

No built-in error handling.

Limited composition.

Uses any for some operations.

Error handling

lo-errs.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)
}
Choose samber/lo whenbest fit

Simple collection operations.

No complex error handling needed.

Team unfamiliar with FP.

Performance is critical.

Choose fp-go whenbest fit

Complex error handling.

Need monadic composition.

Building data pipelines.

Type safety is paramount.

03

fp-go vs. go-functional.

go-functional is a functional library built around iterators and monadic types.

go-functional.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 },
)
Afterpros

Monadic types.

Iterator-based (lazy).

Good type safety.

Beforecons

Limited documentation.

Smaller ecosystem.

Less active development.

Fewer types than fp-go.

04

fp-go vs. mo.

mo is a lightweight FP library focused on Option, Result, and Either.

mo.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)
Afterpros

Simple API.

Good documentation.

Easy to learn.

Good performance.

Beforecons

Limited types (Option, Result, Either).

No IO or Reader types.

Limited composition utilities.

No lazy evaluation.

05

Feature deep dive.

Error handling

LibraryApproachComposition
fp-goResult/Either types
Type safety: excellent. Composition: excellent.
samber/loManual (Go style)
Type safety: good. Composition: none.
go-functionalResult type
Type safety: good. Composition: good.
moResult/Either types
Type safety: good. Composition: limited.

Collection operations

LibraryOperationsNotes
fp-go30+ operations
Good performance (idiomatic: excellent). Type safety: excellent.
samber/lo100+ operations
Excellent performance. Uses any.
go-functional20+ operations
Performance unknown. Type safety: good.
mo10+ operations
Good performance. Type safety: good.

Monadic types

LibraryTypes available
fp-goResult, Either, Option, IO, IOResult, IOEither, IOOption, Reader, ReaderIO, ReaderIOResult, State, Lazy, Identity, Effect, and more
samber/loNone
go-functionalOption, Result, Iterator
moOption, Result, Either, Future, Task

Composition utilities

LibraryAvailableNotes
fp-goPipe, Flow, Compose, custom
Many composition combinators.
samber/loLimited
No first-class Pipe/Flow/Compose.
go-functionalPipe, Flow
Some.
moLimited
No first-class Pipe/Flow/Compose.
06

Use-case recommendations.

Use caseWinnerSnippet
Simple collection opssamber/lo
lo.Map(lo.Filter(items, predicate), transform)
Complex error handlingfp-go
function.Pipe4(step1(), result.Chain(step2), result.Chain(step3), result.Chain(step4))
Lightweight Option/Resultmo
mo.Some(value) · mo.Ok[int](42)
Full FP applicationfp-go
readerioresult.Ask + Chain pipelines
Iterator-based processinggo-functional or fp-go
it.Map(it.Filter(it.Lift(data), pred), transform)
07

Migration paths.

From samber/lo to fp-go

before.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
  },
)

From mo to fp-go

before.go
import "github.com/samber/mo"

opt := mo.Some(42)
value := opt.OrElse(0)
08

Performance comparison.

Filter · 1M ops$go test -bench=. -benchmem
VariantSpeedns/opB/opΔ
stdlib1,050baseline
samber/lo1,100+5%
fp-go (idiomatic)1,080+3%
fp-go (standard)1,200+14%
Bottom line.
  • 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
09

Decision matrix.

Choose fp-goif you need

Full FP toolkit.

Complex error handling.

IO and effect management.

Reader pattern (dependency injection).

Comprehensive documentation.

Production support.

Choose samber/loif you need

Simple collection operations.

Minimal learning curve.

Maximum performance.

Wide community adoption.

Quick wins.

Choose moif you need

Lightweight Option/Result.

Simple API.

Minimal dependencies.

Quick adoption.

Choose go-functionalif you need

Iterator-based operations.

Lazy evaluation.

Simpler than fp-go.

Basic monadic types.

10

Can you use multiple libraries?

Yes. They coexist.
coexist.go
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)
Best practice.
  • 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.
11

Summary

AspectBest choice
Simplicitysamber/lo or mo
Type safetyfp-go
Performancesamber/lo or fp-go idiomatic
Error handlingfp-go
Learning curvesamber/lo
Compositionfp-go
Documentationfp-go or samber/lo
Production usefp-go or samber/lo
Full FPfp-go