golang-samber-mo

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
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.
角色定位: 你是一名为Go引入函数式编程安全性的Go工程师。你使用单子类型让不可能的状态无法被表示——空值检查变为类型约束,错误处理变为可组合的管道。
思考模式: 在设计多步骤的Option/Result/Either管道时使用
ultrathink
。错误的类型选择会导致不必要的包装/解包,违背单子类型的设计初衷。

samber/mo — Monads and Functional Abstractions for Go

samber/mo — Go语言的单子类型与函数式抽象

Go 1.18+ library providing type-safe monadic types with zero dependencies. Inspired by Scala, Rust, and fp-ts.
Official Resources:
This skill is not exhaustive. Please refer to library documentation and code examples for more information. Context7 can help as a discoverability platform.
bash
go get github.com/samber/mo
For an introduction to functional programming concepts and why monads are valuable in Go, see Monads Guide.
这是一个适用于Go 1.18+的库,提供零依赖的类型安全单子类型。灵感来源于Scala、Rust和fp-ts。
官方资源:
本技能内容并非详尽无遗。如需更多信息,请参考库文档和代码示例。Context7可作为发现平台提供帮助。
bash
go get github.com/samber/mo
如需了解函数式编程概念以及单子类型在Go中的价值,请查看单子类型指南

Core Types at a Glance

核心类型概览

TypePurposeThink of it as...
Option[T]
Value that may be absentRust's
Option
, Java's
Optional
Result[T]
Operation that may failRust's
Result<T, E>
, replaces
(T, error)
Either[L, R]
Value of one of two typesScala's
Either
, TypeScript discriminated union
EitherX[L, R]
Value of one of X typesScala's
Either
, TypeScript discriminated union
Future[T]
Async value not yet availableJavaScript
Promise
IO[T]
Lazy synchronous side effectHaskell's
IO
Task[T]
Lazy async computationfp-ts
Task
State[S, A]
Stateful computationHaskell's
State
monad
类型用途可以类比为...
Option[T]
可能不存在的值Rust的
Option
、Java的
Optional
Result[T]
可能失败的操作Rust的
Result<T, E>
,替代
(T, error)
模式
Either[L, R]
两种类型之一的值Scala的
Either
、TypeScript可辨识联合类型
EitherX[L, R]
X种类型之一的值Scala的
Either
、TypeScript可辨识联合类型
Future[T]
尚未就绪的异步值JavaScript的
Promise
IO[T]
惰性同步副作用Haskell的
IO
Task[T]
惰性异步计算fp-ts的
Task
State[S, A]
带状态的计算Haskell的
State
单子

Option[T] — Nullable Values Without nil

Option[T] — 无需nil的可空值

Represents a value that is either present (
Some
) or absent (
None
). Eliminates nil pointer risks at the type level.
go
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.
表示值存在(
Some
)或不存在(
None
)。在类型层面消除空指针风险。
go
import "github.com/samber/mo"

name := mo.Some("Alice")          // 带值的Option[string]
empty := mo.None[string]()        // 无值的Option[string]
fromPtr := mo.PointerToOption(ptr) // 空指针转换为None

// 安全提取值
name.OrElse("Anonymous")  // "Alice"
empty.OrElse("Anonymous")  // "Anonymous"

// 仅当值存在时转换,不存在则跳过
upper := name.Map(func(s string) (string, bool) {
    return strings.ToUpper(s), true
})
核心方法:
Some
None
Get
MustGet
OrElse
OrEmpty
Map
FlatMap
Match
ForEach
ToPointer
IsPresent
IsAbsent
Option实现了
json.Marshaler/Unmarshaler
sql.Scanner
driver.Valuer
——可直接用于JSON结构体和数据库模型中。
完整API参考请查看Option参考文档

Result[T] — Error Handling as Values

Result[T] — 以值的形式处理错误

Represents success (
Ok
) or failure (
Err
). Equivalent to
Either[error, T]
but specialized for Go's error pattern.
go
// 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
:
go
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.
表示成功(
Ok
)或失败(
Err
)。等价于
Either[error, T]
,但专门适配Go的错误模式。
go
// 包装Go的(value, error)模式
result := mo.TupleToResult(os.ReadFile("config.yaml"))

// 同类型转换——错误会自动短路处理
upper := mo.Ok("hello").Map(func(s string) (string, error) {
    return strings.ToUpper(s), nil
})
// 结果为Ok("HELLO")

// 带回退的提取
val := upper.OrElse("default")
Go语言限制: 直接方法(
.Map
.FlatMap
)无法更改类型参数——
Result[T].Map
返回
Result[T]
,而非
Result[U]
。Go的方法无法引入新的类型参数。如需进行类型转换(例如从
Result[[]byte]
转换为
Result[Config]
),请使用子包函数或
mo.Do
go
import "github.com/samber/mo/result"

// 类型转换管道:[]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) }),
)
核心方法:
Ok
Err
Errf
TupleToResult
Try
Get
MustGet
OrElse
Map
FlatMap
MapErr
Match
ForEach
ToEither
IsOk
IsError
完整API参考请查看Result参考文档

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

Either[L, R] — 两种类型的可辨识联合

Represents a value that is one of two possible types. Unlike Result, neither side implies success or failure — both are valid alternatives.
go
// 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.
表示属于两种可能类型之一的值。与Result不同,它的任意一侧都不代表成功或失败——两侧都是有效的可选值。
go
// 返回缓存数据或新鲜数据的API
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))
}

// 模式匹配
result.Match(
    func(cached CachedUser) mo.Either[CachedUser, FreshUser] { /* 使用缓存数据 */ },
    func(fresh FreshUser) mo.Either[CachedUser, FreshUser] { /* 使用新鲜数据 */ },
)
Either与Result的使用场景区分: 当其中一条路径是错误时,使用
Result[T]
。当两条路径都是有效可选方案时(缓存数据 vs 新鲜数据、左 vs 右、策略A vs 策略B),使用
Either[L, R]
Either3[T1, T2, T3]
Either4
Either5
将此扩展为3-5种类型变体。
完整API参考请查看Either参考文档

Do Notation — Imperative Style with Monadic Safety

Do符号 — 兼具单子安全性的命令式风格

mo.Do
wraps imperative code in a
Result
, catching panics from
MustGet()
calls:
go
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.
mo.Do
将命令式代码包装在
Result
中,捕获
MustGet()
调用引发的恐慌:
go
result := mo.Do(func() int {
    // MustGet在None/Err时会恐慌——Do会将其捕获为Result错误
    a := mo.Some(21).MustGet()
    b := mo.Ok(2).MustGet()
    return a * b  // 42
})
// result为Ok(42)

result := mo.Do(func() int {
    val := mo.None[int]().MustGet()  // 引发恐慌
    return val
})
// result为Err("no such element")
Do符号连接了命令式Go风格与单子安全性——编写线性代码,自动获得错误传播能力。

Pipeline Sub-Packages vs Direct Chaining

管道子包 vs 直接链式调用

samber/mo provides two ways to compose operations:
Direct methods (
.Map
,
.FlatMap
) — work when the output type equals the input type:
go
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:
go
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:
go
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.
samber/mo提供两种组合操作的方式:
直接方法
.Map
.FlatMap
)——适用于输出类型与输入类型相同的场景:
go
opt := mo.Some(42)
doubled := opt.Map(func(v int) (int, bool) {
    return v * 2, true
})  // Option[int]
子包函数
option.Map
result.Map
)——当输出类型与输入类型不同时必须使用:
go
import "github.com/samber/mo/option"

// int -> string类型转换:使用子包的Map
strOpt := option.Map(func(v int) string {
    return fmt.Sprintf("value: %d", v)
})(mo.Some(42))  // Option[string]
管道函数
option.Pipe3
result.Pipe3
)——以可读的方式链式调用多个类型转换操作:
go
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]()
    }),
)
经验法则: 同类型转换使用直接方法。当步骤间存在类型变化时,使用子包函数+管道。
详细的管道API参考请查看管道参考文档

Common Patterns

常见模式

JSON API responses with Option

结合Option的JSON API响应

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

Database nullable columns

数据库可空列

go
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)
go
type User struct {
    ID       int
    Email    string
    Phone    mo.Option[string]  // 实现了sql.Scanner + driver.Valuer
}

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

Wrapping existing Go APIs

包装现有Go API

go
// 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)
}
go
// 将map查找转换为Option
func MapGet[K comparable, V any](m map[K]V, key K) mo.Option[V] {
    return mo.TupleToOption(m[key])  // m[key]返回(V, bool)
}

Uniform extraction with Fold

使用Fold统一提取值

mo.Fold
works uniformly across Option, Result, and Either via the
Foldable
interface:
go
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"
mo.Fold
通过
Foldable
接口在Option、Result和Either上统一工作:
go
str := mo.Fold[error, int, string](
    mo.Ok(42),  // 适用于Option、Result或Either
    func(v int) string { return fmt.Sprintf("got %d", v) },
    func(err error) string { return "failed" },
)
// "got 42"

Best Practices

最佳实践

  1. 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
  2. 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
  3. Use
    Result[T]
    for errors,
    Either[L, R]
    for alternatives
    — Result is specialized for success/failure; Either is for two valid types
  4. Option for nullable fields, not zero values
    Option[string]
    distinguishes "absent" from "empty string"; use plain
    string
    when empty string is a valid value
  5. Chain, don't nest
    result.Map(...).FlatMap(...).OrElse(default)
    reads left-to-right; avoid nested if/else patterns when monadic chaining is cleaner
  6. 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.
If you encounter a bug or unexpected behavior in samber/mo, open an issue at https://github.com/samber/mo/issues.
  1. 优先使用
    OrElse
    而非
    MustGet
    ——
    MustGet
    在值不存在/出错时会恐慌;仅在
    mo.Do
    块中使用(恐慌会被捕获为Result错误),或在确定值一定存在时使用
  2. 在API边界使用
    TupleToResult
    —— 在边界将Go的
    (T, error)
    转换为
    Result[T]
    ,然后在领域逻辑中使用
    Map
    /
    FlatMap
    链式调用
  3. 错误场景用
    Result[T]
    ,可选方案用
    Either[L, R]
    —— Result专门用于成功/失败场景;Either用于两种有效类型的场景
  4. 可空字段用Option,零值场景用普通类型 ——
    Option[string]
    区分"不存在"与"空字符串";当空字符串是有效值时,使用普通
    string
    类型
  5. 链式调用而非嵌套 ——
    result.Map(...).FlatMap(...).OrElse(default)
    从左到右可读性更强;当单子链式调用更简洁时,避免嵌套if/else模式
  6. 多步骤类型转换使用子包管道 —— 当3个及以上步骤都涉及类型变化时,
    option.Pipe3(...)
    比嵌套函数调用更具可读性
如需了解高级类型(Future、IO、Task、State),请查看高级类型参考文档
如果在使用samber/mo时遇到bug或意外行为,请在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
  • -> 查看
    samber/cc-skills-golang@golang-samber-lo
    技能,了解与mo类型兼容的函数式集合转换(切片的Map、Filter、Reduce)
  • -> 查看
    samber/cc-skills-golang@golang-error-handling
    技能,了解Go语言惯用的错误处理模式
  • -> 查看
    samber/cc-skills-golang@golang-safety
    技能,了解空值安全与防御性Go编码
  • -> 查看
    samber/cc-skills-golang@golang-database
    技能,了解数据库访问模式
  • -> 查看
    samber/cc-skills-golang@golang-design-patterns
    技能,了解函数式选项及其他Go模式