gomad

package module
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 6, 2026 License: MIT Imports: 9 Imported by: 0

README

gomad

Go Reference MIT License Runnable Examples

Rust-style Option and Result ergonomics, designed for modern Go.

Quick start · Why gomad · API reference · Examples · 简体中文

gomad is a lightweight Option and Result library for Go 1.27+, built around generic methods and zero-cost value types.

It brings explicit optional values and typed failures to Go while keeping the API familiar: values are plain structs, zero values are valid, and adapters are provided for (value, ok), (value, error), JSON, database/sql, and the standard errors package.

If you are looking for type-safe null handling, composable Go error handling, or Rust-inspired functional primitives without abandoning Go conventions, gomad is built for that exact middle ground.

Highlights

What you get
Go 1.27 native Type-changing generic method chains such as Option[int].Map(...) -> Option[string]
Go-friendly boundaries Adapters for pointers, map lookups, (T, error), errors.Is/As, JSON, and SQL
Predictable values Valid zero values, no hidden global state, and no mandatory heap allocation
Small dependency surface Standard library only; no runtime framework or code generation
Executable documentation Every public Option, Result, and Iterator API has a tested example

30-second tour

name := option.Some(1).
	AndThen(findUser).
	Map(func(user User) string { return user.Name }).
	Filter(func(name string) bool { return name != "" })

config := result.From(os.ReadFile("config.json")).
	Map(parseConfig).
	AndThen(validateConfig).
	Inspect(func(Config) { log.Println("config loaded") }).
	InspectErr(func(err error) { log.Println("config error:", err) })

Both chains remain statically typed from end to end. None and Err branches skip success callbacks automatically, so the happy path stays readable without hiding failure handling.

Why

Pointers overload “absent” with allocation and mutability, while (T, error) pairs are easy to accidentally separate. Option[T] and Result[T, E] keep the state and payload together, make every branch explicit, and compose with Go 1.27 generic method chains.

gomad is a good fit for parsers, configuration loaders, API clients, database boundaries, validation pipelines, and domain models where “missing” and “failed” should be impossible to confuse with ordinary values.

Install

go get github.com/23jdd/gomad@latest

Your module must use Go 1.27 or newer:

module example.com/myapp

go 1.27

Option

value := option.Some(10)

if value.IsSome() {
	fmt.Println(value.Unwrap())
}

fallback := option.None[int]().UnwrapOr(42)

The zero value of Option[T] is None. Constructors cover common Go forms:

fromPointer := option.FromPtr(ptr)
fromLookup := option.FromValueOk(value, ok)
fromCall := option.FromValueError(value, err)

A map lookup must first bind its comma-ok result because Go does not pass a map index as a two-value function argument:

value, ok := values[key]
found := option.FromValueOk(value, ok)

Result

func divide(a, b int) result.Result[int, error] {
	if b == 0 {
		return result.Err[int](errors.New("division by zero"))
	}
	return result.Ok[int, error](a / b)
}

Use From with ordinary Go functions that return (T, error):

contents := result.From(os.ReadFile("config.json"))
number := result.From(strconv.Atoi("42"))

The zero value of Result[T, E] is Err containing the zero value of E.

Map

Go 1.27 generic methods let a chain change its value type without losing static type safety:

length := option.Some(10).
	Map(strconv.Itoa).
	Map(func(value string) int { return len(value) })

text := result.From(strconv.Atoi("21")).
	Map(func(value int) int { return value * 2 }).
	Map(strconv.Itoa)

Package-level option.Map, result.Map, and result.MapErr equivalents are also available when function composition is more convenient.

AndThen

Use AndThen when the callback already returns an Option or Result:

name := option.Some(1).
	AndThen(findUser).
	Map(func(user User) string { return user.Name }).
	Filter(func(name string) bool { return name != "" })

config := result.From(os.ReadFile("config.json")).
	Map(parseConfig).
	AndThen(validateConfig)

OrElse provides lazy recovery. Inspect and InspectErr observe a chain without changing its value.

Error handling

Convert between the two types without unpacking them:

required := optionalValue.OkOr(errors.New("value is required"))
optional := required.Ok()
failure := required.Err()

For Result[T, error], standard error traversal works directly:

wrapped := result.From(load()).Wrap("load configuration")

if errors.Is(wrapped.IntoError(), fs.ErrNotExist) {
	// handle a missing file
}

err := wrapped.IntoError() // nil for Ok, an error for Err

errors.As is supported in the same way. The package-level result.Error helper is equivalent to IntoError, and Wrap adds context with %w semantics. Result itself deliberately does not implement error: its required Unwrap() T value method would conflict with the standard Unwrap() error error-chain convention.

Option vs pointer

Use a pointer when identity, shared mutation, or a large object makes pointer semantics meaningful. Use Option[T] when the important fact is simply whether a value exists. Option cannot be confused with a present nil pointer, has a valid zero value, and simple values stay allocation-free.

Result vs (T, error)

Use ordinary (T, error) at conventional Go API boundaries. Convert to Result when several transformations need to be composed, a typed non-error failure is useful, or the combined value needs to be stored or passed around. result.From and IntoError make both styles interoperable.

Collections and iterators

values := []result.Result[int, error]{
	result.Ok[int, error](1),
	result.Ok[int, error](2),
}
collected := result.Collect(values) // Result[[]int, error]

doubled := option.Some(10).
	Iter().
	Map(func(value int) int { return value * 2 }).
	Filter(func(value int) bool { return value > 10 }).
	Collect()

Option provides Collect, All, and Any. Result provides Collect, All, and Partition.

JSON and SQL

Option uses the natural nullable JSON representation:

Some("hello") -> "hello"
None           -> null

Result uses an object with exactly one branch:

{"ok": 42}
{"err": "message"}

Option[T] implements sql.Scanner and driver.Valuer; SQL NULL maps to None. Database-native scalar types and types implementing sql.Scanner, driver.Valuer, or encoding.TextUnmarshaler are supported.

Go 1.27

The minimum version is Go 1.27 because Map, MapErr, AndThen, and OrElse declare method type parameters. This enables a single chain to move from Option[int] to Option[string], or from Result[T, E] to Result[U, E].

If multiple Go installations are on PATH, ensure gofmt also comes from Go 1.27; older formatters reject generic method syntax even when go test selects the correct toolchain automatically.

Benchmarks

Run the complete validation suite with:

go test ./...
go test ./... -race
go test -run '^$' -bench Benchmark -benchmem ./option ./result

On windows/amd64 with an Intel Core i7-14650HX and Go 1.27.1, the included microbenchmarks report zero allocations for Option.Map, Result.Map, map lookup conversion, and result.From. Benchmark timings vary by machine; run them locally before drawing performance conclusions.

API reference

Area API
Option constructors Some, None, FromPtr, FromZero, FromValueOk, FromValueError
Option state IsSome, IsNone, Get, Unwrap, Expect, UnwrapOr, UnwrapOrElse
Option composition Map, AndThen, OrElse, Filter, Inspect, Flatten, OkOr, OkOrElse, Iter
Option collections Collect, All, Any, Match
Result constructors Ok, Err, From
Result state IsOk, IsErr, Get, Unwrap, UnwrapErr, Expect, ExpectErr, UnwrapOr, UnwrapOrElse,Must
Result composition Map, MapErr, AndThen, OrElse, Inspect, InspectErr, Ok, Err, Iter
Result errors Error, IntoError, Wrap (with standard errors.Is and errors.As)
Result collections Collect, All, Partition, Match
Iterator Empty, Once, FromSlice, Map, Filter, Collect, Len

See examples/main.go for a runnable end-to-end example.

Support and contribute

If gomad makes your Go code clearer, consider starring the repository. Stars help other Go developers discover the project.

  • Run the end-to-end example or browse the executable Option, Result, and Iterator examples.
  • Open an issue for bugs, API ideas, or real-world integration gaps.
  • Pull requests with focused tests and examples are welcome.
  • Share gomad with teams exploring explicit optional values or composable error handling in Go.

Documentation

Overview

Package gomad 定义 Option 与 Result 的共享值类型。

通常应从子包 option 和 result 使用这些类型;根包用于消除两个类型互相 转换时的循环依赖。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Option

type Option[T any] struct {
	// contains filtered or unexported fields
}

Option 表示一个可能存在的值:Some 包含值,None 不包含值。 Option 的零值等价于 None。

func None

func None[T any]() Option[T]

None 创建一个不包含值的 Option。

func Some

func Some[T any](value T) Option[T]

Some 创建一个包含 value 的 Option。

func (Option[T]) AndThen

func (opt Option[T]) AndThen[U any](transform func(T) Option[U]) Option[U]

AndThen 串联另一个返回 Option 的操作,并允许改变值类型。

func (Option[T]) Expect

func (opt Option[T]) Expect(message string) T

Expect 返回 Some 中的值;对 None 调用时使用 message 触发 panic。

func (Option[T]) Filter

func (opt Option[T]) Filter(predicate func(T) bool) Option[T]

Filter 仅在谓词接受 Some 中的值时保留该值。

func (Option[T]) Flatten

func (opt Option[T]) Flatten() T

Flatten 去掉一层 Option 嵌套。 泛型代码中建议优先使用 option.Flatten,以获得更严格的类型约束。

func (Option[T]) Get

func (opt Option[T]) Get() (T, bool)

Get 返回内部值以及表示值是否存在的布尔值。

func (Option[T]) Inspect

func (opt Option[T]) Inspect(inspect func(T)) Option[T]

Inspect 在 Some 分支执行只读回调,并返回原 Option。

func (Option[T]) IsNone

func (opt Option[T]) IsNone() bool

IsNone 判断 Option 是否不包含值。

func (Option[T]) IsSome

func (opt Option[T]) IsSome() bool

IsSome 判断 Option 是否包含值。

func (Option[T]) Iter

func (opt Option[T]) Iter() iterator.Iterator[T]

Iter 将 Some 转为单元素迭代器,将 None 转为空迭代器。

func (Option[T]) Map

func (opt Option[T]) Map[U any](transform func(T) U) Option[U]

Map 转换 Some 中的值,并允许改变值类型;None 会直接传播。

func (Option[T]) MarshalJSON

func (opt Option[T]) MarshalJSON() ([]byte, error)

MarshalJSON 将 Some 编码为内部值,将 None 编码为 null。

func (Option[T]) OkOr

func (opt Option[T]) OkOr[E any](err E) Result[T, E]

OkOr 将 Some 转为 Ok,将 None 转为包含 err 的 Err。

func (Option[T]) OkOrElse

func (opt Option[T]) OkOrElse[E any](fallback func() E) Result[T, E]

OkOrElse 将 Some 转为 Ok,None 则调用 fallback 生成 Err。

func (Option[T]) OrElse

func (opt Option[T]) OrElse(fallback func() Option[T]) Option[T]

OrElse 为 None 延迟生成另一个 Option,Some 保持不变。

func (*Option[T]) Scan

func (opt *Option[T]) Scan(source any) error

Scan 实现 sql.Scanner;SQL NULL 会转换为 None。

func (*Option[T]) UnmarshalJSON

func (opt *Option[T]) UnmarshalJSON(data []byte) error

UnmarshalJSON 将 null 解码为 None,其他 JSON 值解码为 Some。

func (Option[T]) Unwarp

func (opt Option[T]) Unwarp() T

Unwarp 为早期版本的拼写错误保留源码兼容性。 Deprecated: 请使用 Unwrap。

func (Option[T]) Unwrap

func (opt Option[T]) Unwrap() T

Unwrap 返回 Some 中的值;对 None 调用时会触发 panic。

func (Option[T]) UnwrapOr

func (opt Option[T]) UnwrapOr(fallback T) T

UnwrapOr 返回 Some 中的值,None 则返回 fallback。

func (Option[T]) UnwrapOrElse

func (opt Option[T]) UnwrapOrElse(fallback func() T) T

UnwrapOrElse 返回 Some 中的值,None 则调用 fallback 生成默认值。

func (Option[T]) Value

func (opt Option[T]) Value() (driver.Value, error)

Value 实现 driver.Valuer;None 会转换为 SQL NULL。

type Result

type Result[T, E any] struct {
	// contains filtered or unexported fields
}

Result 表示一次可能成功或失败的计算:Ok 包含成功值,Err 包含错误值。 Result 的零值是包含 E 零值的 Err。

func Err

func Err[T, E any](err E) Result[T, E]

Err 创建一个包含错误值的 Result。

func Ok

func Ok[T, E any](value T) Result[T, E]

Ok 创建一个包含成功值的 Result。

func (Result[T, E]) AndThen

func (result Result[T, E]) AndThen[U any](transform func(T) Result[U, E]) Result[U, E]

AndThen 串联另一个返回 Result 的操作,并允许改变成功值类型。

func (Result[T, E]) Err

func (result Result[T, E]) Err() Option[E]

Err 将错误分支转为 Some,将成功分支转为 None。

func (Result[T, E]) Expect

func (result Result[T, E]) Expect(message string) T

Expect 返回 Ok 中的值;对 Err 调用时使用 message 触发 panic。

func (Result[T, E]) ExpectErr

func (result Result[T, E]) ExpectErr(message string) E

ExpectErr 返回 Err 中的错误值;对 Ok 调用时使用 message 触发 panic。

func (Result[T, E]) ExpectMust added in v0.3.0

func (result Result[T, E]) ExpectMust[U any](f func(T) (U, error), message string) Result[U, error]

ExpectMust Must+自定义错误內容

func (Result[T, E]) Get

func (result Result[T, E]) Get() (T, E, bool)

Get 返回成功值、错误值以及是否成功。

func (Result[T, E]) Inspect

func (result Result[T, E]) Inspect(inspect func(T)) Result[T, E]

Inspect 在 Ok 分支执行只读回调,并返回原 Result。

func (Result[T, E]) InspectErr

func (result Result[T, E]) InspectErr(inspect func(E)) Result[T, E]

InspectErr 在 Err 分支执行只读回调,并返回原 Result。

func (Result[T, E]) IntoError

func (result Result[T, E]) IntoError() error

IntoError 将 Ok 转为 nil,将 Err 转为标准 error。

func (Result[T, E]) IsErr

func (result Result[T, E]) IsErr() bool

IsErr 判断 Result 是否为错误分支。

func (Result[T, E]) IsOk

func (result Result[T, E]) IsOk() bool

IsOk 判断 Result 是否为成功分支。

func (Result[T, E]) Iter

func (result Result[T, E]) Iter() iterator.Iterator[T]

Iter 将 Ok 转为单元素迭代器,将 Err 转为空迭代器。

func (Result[T, E]) Map

func (result Result[T, E]) Map[U any](transform func(T) U) Result[U, E]

Map 转换 Ok 中的值,并允许改变成功值类型;Err 会直接传播。

func (Result[T, E]) MapErr

func (result Result[T, E]) MapErr[F any](transform func(E) F) Result[T, F]

MapErr 转换 Err 中的值,并允许改变错误值类型;Ok 会直接传播。

func (Result[T, E]) MarshalJSON

func (result Result[T, E]) MarshalJSON() ([]byte, error)

MarshalJSON 将 Result 编码为 {"ok": value} 或 {"err": err}。

func (Result[T, E]) Must added in v0.3.0

func (result Result[T, E]) Must[U any](f func(T) (U, error)) Result[U, error]

Must 将一个 Result转化为另一个Result 相当于Map时如果Error直接panic

func (Result[T, E]) Ok

func (result Result[T, E]) Ok() Option[T]

Ok 将成功分支转为 Some,将错误分支转为 None。

func (Result[T, E]) OrElse

func (result Result[T, E]) OrElse[F any](fallback func(E) Result[T, F]) Result[T, F]

OrElse 恢复 Err 分支,并允许改变错误值类型。

func (*Result[T, E]) UnmarshalJSON

func (result *Result[T, E]) UnmarshalJSON(data []byte) error

UnmarshalJSON 从只包含 ok 或 err 的 JSON 对象解码 Result。

func (Result[T, E]) Unwrap

func (result Result[T, E]) Unwrap() T

Unwrap 返回 Ok 中的值;对 Err 调用时会触发 panic。

func (Result[T, E]) UnwrapErr

func (result Result[T, E]) UnwrapErr() E

UnwrapErr 返回 Err 中的错误值;对 Ok 调用时会触发 panic。

func (Result[T, E]) UnwrapOr

func (result Result[T, E]) UnwrapOr(fallback T) T

UnwrapOr 返回 Ok 中的值,Err 则返回 fallback。

func (Result[T, E]) UnwrapOrElse

func (result Result[T, E]) UnwrapOrElse(fallback func(E) T) T

UnwrapOrElse 返回 Ok 中的值,Err 则调用 fallback 生成默认值。

func (Result[T, E]) Wrap

func (result Result[T, E]) Wrap(message string) Result[T, error]

Wrap 为 Err 添加上下文,并将错误类型统一为 error。

Directories

Path Synopsis
Package iterator 提供 Option 与 Result 使用的小型值迭代器。
Package iterator 提供 Option 与 Result 使用的小型值迭代器。
Package option 提供零分配的 Option 类型及其常用辅助函数。
Package option 提供零分配的 Option 类型及其常用辅助函数。
Package result 提供显式表示成功与失败的 Result 类型及辅助函数。
Package result 提供显式表示成功与失败的 Result 类型及辅助函数。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL