Loading...
Loading...
Monadic types for Golang using samber/mo — Option, Result, Either, Future, IO, Task, and State types for type-safe nullable values, error handling, and functional composition with pipeline sub-packages. Apply when using or adopting samber/mo, when the codebase imports `github.com/samber/mo`, or when considering functional programming patterns as a safety design for Golang.
npx skill4agent add samber/cc-skills-golang golang-samber-moultrathinkgo get github.com/samber/mo| 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 |
SomeNoneimport "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
})SomeNoneGetMustGetOrElseOrEmptyMapFlatMapMatchForEachToPointerIsPresentIsAbsentjson.Marshaler/Unmarshalersql.Scannerdriver.ValuerOkErrEither[error, T]// 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").Map.FlatMapResult[T].MapResult[T]Result[U]Result[[]byte]Result[Config]mo.Doimport "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) }),
)OkErrErrfTupleToResultTryGetMustGetOrElseMapFlatMapMapErrMatchForEachToEitherIsOkIsError// 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 */ },
)Result[T]Either[L, R]Either3[T1, T2, T3]Either4Either5mo.DoResultMustGet()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").Map.FlatMapopt := mo.Some(42)
doubled := opt.Map(func(v int) (int, bool) {
return v * 2, true
}) // Option[int]option.Mapresult.Mapimport "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]option.Pipe3result.Pipe3import "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]()
}),
)type UserResponse struct {
Name string `json:"name"`
Nickname mo.Option[string] `json:"nickname"` // omits null gracefully
Bio mo.Option[string] `json:"bio"`
}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)// 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)
}mo.FoldFoldablestr := 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"OrElseMustGetMustGetmo.DoTupleToResult(T, error)Result[T]MapFlatMapResult[T]Either[L, R]Option[string]stringresult.Map(...).FlatMap(...).OrElse(default)option.Pipe3(...)samber/cc-skills-golang@golang-samber-losamber/cc-skills-golang@golang-error-handlingsamber/cc-skills-golang@golang-safetysamber/cc-skills-golang@golang-databasesamber/cc-skills-golang@golang-design-patterns