Generated banner for Golang Samber Mo
Generated logo for Golang Samber Mo

Golang Samber Mo

Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe

  • Skill
  • Tool integration
  • Software development
  • MIT
  • No issues found

From the author

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. Not for nil-safety and zero-value design without this library (→ See `samber/cc-skills-golang@golang-safety` skill), nor for native error wrapping with fmt.Errorf, errors.Is and errors.As (→ See `samber/cc-skills-golang@golang-error-handling` skill).
— the description by samber

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The samber/mo library introduces functional programming abstractions to Go, enabling developers to replace traditional nil checks and error handling with type-safe monadic structures. By utilizing types like Option, Result, and Either, developers can create more robust, composable pipelines that make impossible states unrepresentable. The library is designed for Go 1.18+ and provides a suite of tools for handling side effects, asynchronous computations, and stateful logic, bridging the gap between imperative Go and functional safety patterns.

What it does

  • Eliminates nil pointer risks using the Option type.
  • Replaces standard (T, error) patterns with the composable Result type.
  • Implements Either for handling multiple valid alternative outcomes.
  • Provides Future, IO, and Task types for managing side effects and asynchronous operations.
  • Enables functional pipeline composition using sub-package functions.
  • Supports Do notation to bridge imperative code with monadic error propagation.

When to use it

  • When you want to enforce type-level safety for nullable values or error states.
  • When building complex data processing pipelines that require clean, composable transformations.
  • When you need to represent multiple valid outcomes or alternative strategies clearly.
  • When adopting functional programming patterns to improve code maintainability and robustness in Go.
  • When you need to handle asynchronous computations or side effects in a structured, lazy manner.

Advantages

  • Reduces runtime panics by replacing nil checks with explicit Option types.
  • Improves code readability by chaining operations instead of nesting if/else blocks.
  • Provides consistent, type-safe error handling across the application.
  • Facilitates cleaner API boundaries by converting native Go errors into Result types.
  • Offers a standard way to handle complex state and side effects without side-effect-heavy imperative code.

Usage examples

“How do I safely handle a nullable string in my struct using samber/mo?”
You can use mo.Option[string] in your struct fields, which allows you to distinguish between an absent value (None) and an empty string, and provides methods like OrElse for safe extraction.
“How can I chain multiple operations that might fail in Go?”
You can use mo.Result[T] and its associated pipeline functions like result.Pipe2 to chain transformations; if any step returns an error, the pipeline short-circuits automatically.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/samber/golang-samber-mo/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip golang-samber-mo.zip -d .agents/skills

Documentation by samber

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: Reason as thoroughly as possible when designing multi-step Option/Result/Either pipelines — wrong type choice creates unnecessary wrapping/unwrapping that defeats the purpose of monads. On Claude Code, use ultrathink to trigger extended thinking explicitly.

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:

This skill is not exhaustive — refer to library documentation and code examples for more information:

  • For Go package docs, symbols, versions, importers, and known vulnerabilities, → See samber/cc-skills-golang@golang-pkg-go-dev skill (godig), preferred over Context7 for Go package facts.

  • To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See samber/cc-skills-golang@golang-gopls skill (gopls).

  • Context7 remains a fallback for docs not indexed on pkg.go.dev.

go get github.com/samber/mo

For an introduction to functional programming concepts and why monads are valuable in Go, see Monads Guide.

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] — 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.

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.

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 := fetchUser("user-123")
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.

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.

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

  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.

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

Files

references/either.md

Either[L, R] API Reference

A discriminated union representing a value of one of two possible types. By convention, Left is the "alternative" path and Right is the "primary" path, but neither implies success or failure.

Table of Contents

  • Constructors

  • Type Checking

  • Value Extraction

  • Transformations

    • Swap — exchange left and right

    • MapLeft / MapRight — transform one side

    • Match — pattern matching

    • ForEach — side effects

  • Either vs Result

  • Either3[T1, T2, T3] — Three-Type Union

    • Constructors

    • Type Checking and Extraction

    • Pattern Matching

    • Transformations

  • Either4 and Either5

Constructors

FunctionDescription
mo.Left[L, R](value L)Creates a left-side Either
mo.Right[L, R](value R)Creates a right-side Either

Type Checking

MethodReturnsDescription
IsLeft()boolTrue if the value is on the left side
IsRight()boolTrue if the value is on the right side

Value Extraction

MethodReturnsDescription
Left()(L, bool)Left value and whether it exists
Right()(R, bool)Right value and whether it exists
MustLeft()LLeft value or panics
MustRight()RRight value or panics
LeftOrElse(fallback L)LLeft value or fallback
RightOrElse(fallback R)RRight value or fallback
LeftOrEmpty()LLeft value or zero value
RightOrEmpty()RRight value or zero value
Unpack()(L, R)Both values (one will be zero value)

Transformations

Swap — exchange left and right
e := mo.Left[string, int]("hello")
swapped := e.Swap()  // Either[int, string] — Right("hello")
MapLeft / MapRight — transform one side

The callback receives the value and must return a new Either[L, R]:

e := mo.Left[string, int]("hello")
upper := e.MapLeft(func(s string) mo.Either[string, int] {
    return mo.Left[string, int](strings.ToUpper(s))
})
// Left("HELLO")

e2 := mo.Right[string, int](42)
doubled := e2.MapRight(func(v int) mo.Either[string, int] {
    return mo.Right[string, int](v * 2)
})
// Right(84)

Go limitation: Like Option.Map and Result.Map, direct Either methods cannot change the type parameters. Use sub-package either.MapLeft/either.MapRight for type-changing transforms — see Pipelines Reference.

Match — pattern matching
e.Match(
    func(left string) mo.Either[string, int] {
        fmt.Println("Left:", left)
        return mo.Left[string, int](left)
    },
    func(right int) mo.Either[string, int] {
        fmt.Println("Right:", right)
        return mo.Right[string, int](right)
    },
)
ForEach — side effects
e.ForEach(
    func(left string) { fmt.Println("Left:", left) },
    func(right int) { fmt.Println("Right:", right) },
)

Either vs Result

FeatureEither[L, R]Result[T]
Left/Err typeAny type LAlways error
SemanticsTwo valid alternativesSuccess or failure
Use caseCached vs fresh, A vs BOperation that may fail
JSONNot supportedJSON-RPC format

Result[T] is equivalent to Either[error, T] — use result.ToEither() to convert.

Either3[T1, T2, T3] — Three-Type Union

Constructors
e := mo.NewEither3Arg1[string, int, bool]("hello")  // T1 variant
e := mo.NewEither3Arg2[string, int, bool](42)         // T2 variant
e := mo.NewEither3Arg3[string, int, bool](true)       // T3 variant
Type Checking and Extraction
e.IsArg1()  // true if T1
e.IsArg2()  // true if T2
e.IsArg3()  // true if T3

val, ok := e.Arg1()      // (T1, bool)
val := e.MustArg1()      // T1 or panics
val := e.Arg1OrElse(fb)  // T1 or fallback
val := e.Arg1OrEmpty()   // T1 or zero value
t1, t2, t3 := e.Unpack() // all three (two will be zero)
Pattern Matching
e.Match(
    func(s string) mo.Either3[string, int, bool] { ... },
    func(i int) mo.Either3[string, int, bool] { ... },
    func(b bool) mo.Either3[string, int, bool] { ... },
)
Transformations

MapArg callbacks receive the value and return a new Either3:

e.MapArg1(func(s string) mo.Either3[string, int, bool] {
    return mo.NewEither3Arg1[string, int, bool](strings.ToUpper(s))
})
e.MapArg2(func(i int) mo.Either3[string, int, bool] {
    return mo.NewEither3Arg2[string, int, bool](i * 2)
})

Either4 and Either5

Follow the exact same pattern as Either3 with 4 and 5 type parameters respectively:

  • Either4[T1, T2, T3, T4]: NewEither4Arg1 through NewEither4Arg4, IsArg1-IsArg4, MapArg1-MapArg4

  • Either5[T1, T2, T3, T4, T5]: NewEither5Arg1 through NewEither5Arg5, IsArg1-IsArg5, MapArg1-MapArg5

Use Either3+ when you need a type-safe union of multiple types — for example, an API that returns different response shapes depending on the request type.

Requirements and compatibility

Written for: Claude Code, OpenAI Codex

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Declares allowed tools, Claude Code frontmatter

The author says: Designed for Claude Code, Codex or similar harness, and for projects using Golang.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
samber
Publisher
Samuel Berthe (Community)
Distributed by
Samuel Berthe
Source
samber/cc-skills-golang · skills/golang-samber-mo · commit bb1d43d
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by samber or Samuel Berthe.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download golang-samber-mo.zip