golang-samber-mo
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePersona: 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 when designing multi-step Option/Result/Either pipelines. Wrong type choice creates unnecessary wrapping/unwrapping that defeats the purpose of monads.
ultrathink角色定位: 你是一名为Go引入函数式编程安全性的Go工程师。你使用单子类型让不可能的状态无法被表示——空值检查变为类型约束,错误处理变为可组合的管道。
思考模式: 在设计多步骤的Option/Result/Either管道时使用。错误的类型选择会导致不必要的包装/解包,违背单子类型的设计初衷。
ultrathinksamber/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/moFor 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
核心类型概览
| Type | Purpose | Think of it as... |
|---|---|---|
| Value that may be absent | Rust's |
| Operation that may fail | Rust's |
| Value of one of two types | Scala's |
| Value of one of X types | Scala's |
| Async value not yet available | JavaScript |
| Lazy synchronous side effect | Haskell's |
| Lazy async computation | fp-ts |
| Stateful computation | Haskell's |
| 类型 | 用途 | 可以类比为... |
|---|---|---|
| 可能不存在的值 | Rust的 |
| 可能失败的操作 | Rust的 |
| 两种类型之一的值 | Scala的 |
| X种类型之一的值 | Scala的 |
| 尚未就绪的异步值 | JavaScript的 |
| 惰性同步副作用 | Haskell的 |
| 惰性异步计算 | fp-ts的 |
| 带状态的计算 | Haskell的 |
Option[T] — Nullable Values Without nil
Option[T] — 无需nil的可空值
Represents a value that is either present () or absent (). Eliminates nil pointer risks at the type level.
SomeNonego
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: , , , , , , , , , , , , .
SomeNoneGetMustGetOrElseOrEmptyMapFlatMapMatchForEachToPointerIsPresentIsAbsentOption implements , , — use it directly in JSON structs and database models.
json.Marshaler/Unmarshalersql.Scannerdriver.ValuerFor full API reference, see Option Reference.
表示值存在()或不存在()。在类型层面消除空指针风险。
SomeNonego
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
})核心方法: 、、、、、、、、、、、、。
SomeNoneGetMustGetOrElseOrEmptyMapFlatMapMatchForEachToPointerIsPresentIsAbsentOption实现了、、——可直接用于JSON结构体和数据库模型中。
json.Marshaler/Unmarshalersql.Scannerdriver.Valuer完整API参考请查看Option参考文档。
Result[T] — Error Handling as Values
Result[T] — 以值的形式处理错误
Represents success () or failure (). Equivalent to but specialized for Go's error pattern.
OkErrEither[error, T]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 (, ) cannot change the type parameter — returns , not . Go methods cannot introduce new type parameters. For type-changing transforms (e.g. to ), use sub-package functions or :
.Map.FlatMapResult[T].MapResult[T]Result[U]Result[[]byte]Result[Config]mo.Dogo
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: , , , , , , , , , , , , , , , .
OkErrErrfTupleToResultTryGetMustGetOrElseMapFlatMapMapErrMatchForEachToEitherIsOkIsErrorFor full API reference, see Result Reference.
表示成功()或失败()。等价于,但专门适配Go的错误模式。
OkErrEither[error, T]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语言限制: 直接方法(、)无法更改类型参数——返回,而非。Go的方法无法引入新的类型参数。如需进行类型转换(例如从转换为),请使用子包函数或:
.Map.FlatMapResult[T].MapResult[T]Result[U]Result[[]byte]Result[Config]mo.Dogo
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) }),
)核心方法: 、、、、、、、、、、、、、、、。
OkErrErrfTupleToResultTryGetMustGetOrElseMapFlatMapMapErrMatchForEachToEitherIsOkIsError完整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 when one path is an error. Use when both paths are valid alternatives (cached vs fresh, left vs right, strategy A vs B).
Result[T]Either[L, R]Either3[T1, T2, T3]Either4Either5For 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的使用场景区分: 当其中一条路径是错误时,使用。当两条路径都是有效可选方案时(缓存数据 vs 新鲜数据、左 vs 右、策略A vs 策略B),使用。
Result[T]Either[L, R]Either3[T1, T2, T3]Either4Either5完整API参考请查看Either参考文档。
Do Notation — Imperative Style with Monadic Safety
Do符号 — 兼具单子安全性的命令式风格
mo.DoResultMustGet()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.DoResultMustGet()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 (, ) — work when the output type equals the input type:
.Map.FlatMapgo
opt := mo.Some(42)
doubled := opt.Map(func(v int) (int, bool) {
return v * 2, true
}) // Option[int]Sub-package functions (, ) — required when the output type differs from input:
option.Mapresult.Mapgo
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 (, ) — chain multiple type-changing transformations readably:
option.Pipe3result.Pipe3go
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.FlatMapgo
opt := mo.Some(42)
doubled := opt.Map(func(v int) (int, bool) {
return v * 2, true
}) // Option[int]子包函数(、)——当输出类型与输入类型不同时必须使用:
option.Mapresult.Mapgo
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.Pipe3result.Pipe3go
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.FoldFoldablego
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.FoldFoldablego
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
最佳实践
- Prefer over
OrElse—MustGetpanics on absent/error values; use it only insideMustGetblocks where panics are caught, or when you are certain the value existsmo.Do - Use at API boundaries — convert Go's
TupleToResultto(T, error)at the boundary, then chain withResult[T]/Mapinside your domain logicFlatMap - Use for errors,
Result[T]for alternatives — Result is specialized for success/failure; Either is for two valid typesEither[L, R] - Option for nullable fields, not zero values — distinguishes "absent" from "empty string"; use plain
Option[string]when empty string is a valid valuestring - Chain, don't nest — reads left-to-right; avoid nested if/else patterns when monadic chaining is cleaner
result.Map(...).FlatMap(...).OrElse(default) - Use sub-package pipes for multi-step type transformations — when 3+ steps each change the type, is more readable than nested function calls
option.Pipe3(...)
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.
- 优先使用而非
OrElse——MustGet在值不存在/出错时会恐慌;仅在MustGet块中使用(恐慌会被捕获为Result错误),或在确定值一定存在时使用mo.Do - 在API边界使用—— 在边界将Go的
TupleToResult转换为(T, error),然后在领域逻辑中使用Result[T]/Map链式调用FlatMap - 错误场景用,可选方案用
Result[T]—— Result专门用于成功/失败场景;Either用于两种有效类型的场景Either[L, R] - 可空字段用Option,零值场景用普通类型 —— 区分"不存在"与"空字符串";当空字符串是有效值时,使用普通
Option[string]类型string - 链式调用而非嵌套 —— 从左到右可读性更强;当单子链式调用更简洁时,避免嵌套if/else模式
result.Map(...).FlatMap(...).OrElse(default) - 多步骤类型转换使用子包管道 —— 当3个及以上步骤都涉及类型变化时,比嵌套函数调用更具可读性
option.Pipe3(...)
如需了解高级类型(Future、IO、Task、State),请查看高级类型参考文档。
如果在使用samber/mo时遇到bug或意外行为,请在https://github.com/samber/mo/issues提交问题。
Cross-References
交叉参考
- -> See skill for functional collection transforms (Map, Filter, Reduce on slices) that compose with mo types
samber/cc-skills-golang@golang-samber-lo - -> See skill for idiomatic Go error handling patterns
samber/cc-skills-golang@golang-error-handling - -> See skill for nil-safety and defensive Go coding
samber/cc-skills-golang@golang-safety - -> See skill for database access patterns
samber/cc-skills-golang@golang-database - -> See skill for functional options and other Go patterns
samber/cc-skills-golang@golang-design-patterns
- -> 查看技能,了解与mo类型兼容的函数式集合转换(切片的Map、Filter、Reduce)
samber/cc-skills-golang@golang-samber-lo - -> 查看技能,了解Go语言惯用的错误处理模式
samber/cc-skills-golang@golang-error-handling - -> 查看技能,了解空值安全与防御性Go编码
samber/cc-skills-golang@golang-safety - -> 查看技能,了解数据库访问模式
samber/cc-skills-golang@golang-database - -> 查看技能,了解函数式选项及其他Go模式
samber/cc-skills-golang@golang-design-patterns