---
id: daily-golang-samber-mo
name: "golang-samber-mo"
url: https://skills.yangsir.net/skill/daily-golang-samber-mo
author: samber
domain: ai-software-architecture-engineering
tags: ["functional-programming", "type-safety", "error-handling", "backend-development", "software-design"]
install_count: 37700
rating: 4.60 (20 reviews)
github: https://github.com/samber/cc-skills-golang
---

# golang-samber-mo

> Go 函数式编程安全增强，使用单子让不可能状态无法表示，将 nil 检查转为类型约束，错误处理变为可组合管道

**Stats**: 37,700 installs · 4.6/5 (20 reviews)

## Before / After 对比

### 运行时错误率

**Before**:

使用可空类型和手动错误处理，nil 指针解引用和状态不一致导致生产环境每月约 20 次运行时 panic

**After**:

通过类型系统在编译期排除非法状态，Option/Result 单子强制显式处理所有分支，生产环境 panic 降至零

| Metric | Before | After | Change |
|---|---|---|---|
| 月度 panic 次数 | 20次 | 0次 | -100% |
| 错误处理代码覆盖率 | 70% | 100% | +43% |

## Readme

# golang-samber-mo

**Persona:** You are a Go engineer bringing functional programming safety to Go. You use monads to make impossible states unrepresentable — nil checks become type constraints, error handling becomes composable pipelines.

**Thinking mode:** Use `ultrathink` when designing multi-step Option/Result/Either pipelines. Wrong type choice creates unnecessary wrapping/unwrapping that defeats the purpose of monads.

# samber/mo — Monads and Functional Abstractions for Go

Go 1.18+ library providing type-safe monadic types with zero dependencies. Inspired by Scala, Rust, and fp-ts.

**Official Resources:**

- [pkg.go.dev/github.com/samber/mo](https://pkg.go.dev/github.com/samber/mo)

- [github.com/samber/mo](https://github.com/samber/mo)

This skill is not exhaustive. Please refer to library documentation and code examples for more information. Context7 can help as a discoverability platform.

```
go get github.com/samber/mo

```

For an introduction to functional programming concepts and why monads are valuable in Go, see [Monads Guide](https://github.com/samber/cc-skills-golang/blob/HEAD/skills/golang-samber-mo/./references/monads-guide.md).

## Core Types at a Glance

Type
Purpose
Think of it as...

`Option[T]`
Value that may be absent
Rust's `Option`, Java's `Optional`

`Result[T]`
Operation that may fail
Rust's `Result<T, E>`, replaces `(T, error)`

`Either[L, R]`
Value of one of two types
Scala's `Either`, TypeScript discriminated union

`EitherX[L, R]`
Value of one of X types
Scala's `Either`, TypeScript discriminated union

`Future[T]`
Async value not yet available
JavaScript `Promise`

`IO[T]`
Lazy synchronous side effect
Haskell's `IO`

`Task[T]`
Lazy async computation
fp-ts `Task`

`State[S, A]`
Stateful computation
Haskell's `State` monad

## Option[T] — Nullable Values Without nil

Represents a value that is either present (`Some`) or absent (`None`). Eliminates nil pointer risks at the type level.

```
import "github.com/samber/mo"

name := mo.Some("Alice")          // Option[string] with value
empty := mo.None[string]()        // Option[string] without value
fromPtr := mo.PointerToOption(ptr) // nil pointer -> None

// Safe extraction
name.OrElse("Anonymous")  // "Alice"
empty.OrElse("Anonymous")  // "Anonymous"

// Transform if present, skip if absent
upper := name.Map(func(s string) (string, bool) {
    return strings.ToUpper(s), true
})

```

**Key methods:** `Some`, `None`, `Get`, `MustGet`, `OrElse`, `OrEmpty`, `Map`, `FlatMap`, `Match`, `ForEach`, `ToPointer`, `IsPresent`, `IsAbsent`.

Option implements `json.Marshaler/Unmarshaler`, `sql.Scanner`, `driver.Valuer` — use it directly in JSON structs and database models.

For full API reference, see [Option Reference](https://github.com/samber/cc-skills-golang/blob/HEAD/skills/golang-samber-mo/./references/option.md).

## Result[T] — Error Handling as Values

Represents success (`Ok`) or failure (`Err`). Equivalent to `Either[error, T]` but specialized for Go's error pattern.

```
// Wrap Go's (value, error) pattern
result := mo.TupleToResult(os.ReadFile("config.yaml"))

// Same-type transform — errors short-circuit automatically
upper := mo.Ok("hello").Map(func(s string) (string, error) {
    return strings.ToUpper(s), nil
})
// Ok("HELLO")

// Extract with fallback
val := upper.OrElse("default")

```

**Go limitation:** Direct methods (`.Map`, `.FlatMap`) cannot change the type parameter — `Result[T].Map` returns `Result[T]`, not `Result[U]`. Go methods cannot introduce new type parameters. For type-changing transforms (e.g. `Result[[]byte]` to `Result[Config]`), use sub-package functions or `mo.Do`:

```
import "github.com/samber/mo/result"

// Type-changing pipeline: []byte -> Config -> ValidConfig
parsed := result.Pipe2(
    mo.TupleToResult(os.ReadFile("config.yaml")),
    result.Map(func(data []byte) Config { return parseConfig(data) }),
    result.FlatMap(func(cfg Config) mo.Result[ValidConfig] { return validate(cfg) }),
)

```

**Key methods:** `Ok`, `Err`, `Errf`, `TupleToResult`, `Try`, `Get`, `MustGet`, `OrElse`, `Map`, `FlatMap`, `MapErr`, `Match`, `ForEach`, `ToEither`, `IsOk`, `IsError`.

For full API reference, see [Result Reference](https://github.com/samber/cc-skills-golang/blob/HEAD/skills/golang-samber-mo/./references/result.md).

## Either[L, R] — Discriminated Union of Two Types

Represents a value that is one of two possible types. Unlike Result, neither side implies success or failure — both are valid alternatives.

```
// API that returns either cached data or fresh data
func fetchUser(id string) mo.Either[CachedUser, FreshUser] {
    if cached, ok := cache.Get(id); ok {
        return mo.Left[CachedUser, FreshUser](cached)
    }
    return mo.Right[CachedUser, FreshUser](db.Fetch(id))
}

// Pattern match
result.Match(
    func(cached CachedUser) mo.Either[CachedUser, FreshUser] { /* use cached */ },
    func(fresh FreshUser) mo.Either[CachedUser, FreshUser] { /* use fresh */ },
)

```

**When to use Either vs Result:** Use `Result[T]` when one path is an error. Use `Either[L, R]` when both paths are valid alternatives (cached vs fresh, left vs right, strategy A vs B).

`Either3[T1, T2, T3]`, `Either4`, and `Either5` extend this to 3-5 type variants.

For full API reference, see [Either Reference](https://github.com/samber/cc-skills-golang/blob/HEAD/skills/golang-samber-mo/./references/either.md).

## Do Notation — Imperative Style with Monadic Safety

`mo.Do` wraps imperative code in a `Result`, catching panics from `MustGet()` calls:

```
result := mo.Do(func() int {
    // MustGet panics on None/Err — Do catches it as Result error
    a := mo.Some(21).MustGet()
    b := mo.Ok(2).MustGet()
    return a * b  // 42
})
// result is Ok(42)

result := mo.Do(func() int {
    val := mo.None[int]().MustGet()  // panics
    return val
})
// result is Err("no such element")

```

Do notation bridges imperative Go style with monadic safety — write straight-line code, get automatic error propagation.

## Pipeline Sub-Packages vs Direct Chaining

samber/mo provides two ways to compose operations:

**Direct methods** (`.Map`, `.FlatMap`) — work when the output type equals the input type:

```
opt := mo.Some(42)
doubled := opt.Map(func(v int) (int, bool) {
    return v * 2, true
})  // Option[int]

```

**Sub-package functions** (`option.Map`, `result.Map`) — required when the output type differs from input:

```
import "github.com/samber/mo/option"

// int -> string type change: use sub-package Map
strOpt := option.Map(func(v int) string {
    return fmt.Sprintf("value: %d", v)
})(mo.Some(42))  // Option[string]

```

**Pipe functions** (`option.Pipe3`, `result.Pipe3`) — chain multiple type-changing transformations readably:

```
import "github.com/samber/mo/option"

result := option.Pipe3(
    mo.Some(42),
    option.Map(func(v int) string { return strconv.Itoa(v) }),
    option.Map(func(s string) []byte { return []byte(s) }),
    option.FlatMap(func(b []byte) mo.Option[string] {
        if len(b) > 0 { return mo.Some(string(b)) }
        return mo.None[string]()
    }),
)

```

**Rule of thumb:** Use direct methods for same-type transforms. Use sub-package functions + pipes when types change across steps.

For detailed pipeline API reference, see [Pipelines Reference](https://github.com/samber/cc-skills-golang/blob/HEAD/skills/golang-samber-mo/./references/pipelines.md).

## Common Patterns

### JSON API responses with Option

```
type UserResponse struct {
    Name     string            `json:"name"`
    Nickname mo.Option[string] `json:"nickname"`  // omits null gracefully
    Bio      mo.Option[string] `json:"bio"`
}

```

### Database nullable columns

```
type User struct {
    ID       int
    Email    string
    Phone    mo.Option[string]  // implements sql.Scanner + driver.Valuer
}

err := row.Scan(&u.ID, &u.Email, &u.Phone)

```

### Wrapping existing Go APIs

```
// Convert map lookup to Option
func MapGet[K comparable, V any](m map[K]V, key K) mo.Option[V] {
    return mo.TupleToOption(m[key])  // m[key] returns (V, bool)
}

```

### Uniform extraction with Fold

`mo.Fold` works uniformly across Option, Result, and Either via the `Foldable` interface:

```
str := mo.Fold[error, int, string](
    mo.Ok(42),  // works with Option, Result, or Either
    func(v int) string { return fmt.Sprintf("got %d", v) },
    func(err error) string { return "failed" },
)
// "got 42"

```

## Best Practices

- **Prefer `OrElse` over `MustGet`** — `MustGet` panics on absent/error values; use it only inside `mo.Do` blocks where panics are caught, or when you are certain the value exists

- **Use `TupleToResult` at API boundaries** — convert Go's `(T, error)` to `Result[T]` at the boundary, then chain with `Map`/`FlatMap` inside your domain logic

- **Use `Result[T]` for errors, `Either[L, R]` for alternatives** — Result is specialized for success/failure; Either is for two valid types

- **Option for nullable fields, not zero values** — `Option[string]` distinguishes "absent" from "empty string"; use plain `string` when empty string is a valid value

- **Chain, don't nest** — `result.Map(...).FlatMap(...).OrElse(default)` reads left-to-right; avoid nested if/else patterns when monadic chaining is cleaner

- **Use sub-package pipes for multi-step type transformations** — when 3+ steps each change the type, `option.Pipe3(...)` is more readable than nested function calls

For advanced types (Future, IO, Task, State), see [Advanced Types Reference](https://github.com/samber/cc-skills-golang/blob/HEAD/skills/golang-samber-mo/./references/advanced-types.md).

If you encounter a bug or unexpected behavior in samber/mo, open an issue at [https://github.com/samber/mo/issues](https://github.com/samber/mo/issues).

## Cross-References

- -> See `samber/cc-skills-golang@golang-samber-lo` skill for functional collection transforms (Map, Filter, Reduce on slices) that compose with mo types

- -> See `samber/cc-skills-golang@golang-error-handling` skill for idiomatic Go error handling patterns

- -> See `samber/cc-skills-golang@golang-safety` skill for nil-safety and defensive Go coding

- -> See `samber/cc-skills-golang@golang-database` skill for database access patterns

- -> See `samber/cc-skills-golang@golang-design-patterns` skill for functional options and other Go patterns

Weekly Installs663Repository[samber/cc-skills-golang](https://github.com/samber/cc-skills-golang)GitHub Stars1.1KFirst SeenMar 22, 2026Security Audits[Gen Agent Trust HubPass](/samber/cc-skills-golang/golang-samber-mo/security/agent-trust-hub)[SocketPass](/samber/cc-skills-golang/golang-samber-mo/security/socket)[SnykPass](/samber/cc-skills-golang/golang-samber-mo/security/snyk)Installed onopencode646cursor639codex637gemini-cli635github-copilot634amp633

---
*Source: https://skills.yangsir.net/skill/daily-golang-samber-mo*
*Markdown mirror: https://skills.yangsir.net/api/skill/daily-golang-samber-mo/markdown*