Go Reference Go Report Card License
mu is a production-ready Go library that brings functional programming primitives to Go with a focus on safety, expressiveness, and seamless integration with existing Go codebases. It provides three core types: Option, Result, and Either.
mu is pronounced "mew" (/mjuː/) and comes from the Greek letter μ (mu). The name represents both "micro" (small, composable building blocks) and the mathematical notation used for monads in functional programming theory. We chose it for its simplicity—just two letters that are easy to type and remember, while connecting to the mathematical foundations of the abstractions this library provides.
- Type Safety: Eliminate nil pointer dereferences and unhandled errors at compile time
- Expressive Code: Write more declarative, chainable operations for complex logic
- Go-Native: Designed specifically for Go's type system and idioms
- Zero Dependencies: No external dependencies beyond Go's standard library
- Production Ready: Thoroughly tested with comprehensive documentation
go get github.com/appthrust/mu
package main import ( "fmt" "github.com/appthrust/mu" ) func main() { // Option: Handle optional values safely user := findUser("john") greeting := user.Map(func(name string) string { return "Hello, " + name + "!" }).OrElse("Hello, stranger!") fmt.Println(greeting) // Result: Chain failable operations result := mu.Ok(42). Map(func(x int) int { return x * 2 }). Map(func(x int) int { return x + 10 }) if value, ok := result.Unwrap(); ok { fmt.Println("Result:", value) // 94 } } func findUser(id string) mu.Option[string] { users := map[string]string{"john": "John Doe"} if name, exists := users[id]; exists { return mu.Some(name) } return mu.None[string]() }
Option[T] represents a value that may or may not be present, providing a type-safe alternative to pointers and nil checks.
Use Cases:
- Optional function parameters
- Database fields that may be NULL
- Configuration values with defaults
- Safe array/slice access
// Constructor functions some := mu.Some(42) // Option containing 42 none := mu.None[int]() // Empty Option // Safe value extraction value := some.OrElse(0) // Returns 42, or 0 if None value = some.OrZero() // Returns 42, or zero value if None // Chaining operations result := some. Filter(func(x int) bool { return x > 0 }). Map(func(x int) int { return x * 2 })
Result[T] represents operations that can succeed with a value or fail with an error. The error type is fixed to Go's built-in error interface for maximum ecosystem compatibility.
Use Cases:
- File I/O operations
- HTTP requests
- Database queries
- Any operation that can fail
// Constructor functions success := mu.Ok(42) // Successful result failure := mu.Err[int](errors.New("failed")) // Failed result // Chain operations (short-circuits on error) result := success. Map(func(x int) int { return x * 2 }). Map(func(x int) int { return x + 10 }) // Convert back to Go's standard pattern value, err := result.Unpack() if err != nil { // handle error }
Either[L, R] represents a value that can be one of two types. This implementation is right-biased, meaning operations like Map work on the Right value by default.
Use Cases:
- Representing different types of responses
- State machines with distinct states
- Validation results with different error types
// Constructor functions left := mu.Left[string, int]("error") // Left value right := mu.Right[string, int](42) // Right value // Right-biased operations (work on Right value) result := right.Map(func(x int) int { return x * 2 }) // Explicit left/right operations leftResult := right.MapLeft(func(s string) string { return "ERROR: " + s })
Due to Go's type system limitations, this library uses a specific pattern to provide both ergonomic and powerful APIs:
Methods (same type transformations):
// T → T transformations use methods for fluent chaining option.Map(func(x int) int { return x * 2 }). Filter(func(x int) bool { return x > 0 })
Functions (type-changing transformations):
// T → U transformations use functions in separate packages import "github.com/appthrust/mu/muo" stringOpt := muo.MapTo(intOpt, func(x int) string { return strconv.Itoa(x) })
This design provides:
- Fluent method chaining for same-type operations
- Powerful type transformations through utility functions
- Clear separation of concerns
Convert between mu.Result and Go's standard (value, error) pattern:
// Go function that returns (T, error) func fetchData() (string, error) { /* ... */ } // Wrap in Result result := mu.Try(fetchData) // Convert back to Go pattern value, err := result.Unpack()
Convert common Go patterns into Option and Result types:
import ( "github.com/appthrust/mu" "github.com/appthrust/mu/muo" "github.com/appthrust/mu/mur" ) // Convert (value, bool) pattern to Option value, ok := someMap["key"] option := muo.From(value, ok) // Convert slice access to Option numbers := []int{1, 2, 3} first := muo.FromSlice(numbers) // Some(1) empty := muo.FromSlice([]int{}) // None // Convert map lookup to Option users := map[string]string{"john": "admin"} role := muo.FromMap(users, "john") // Some("admin") missing := muo.FromMap(users, "bob") // None // Convert (value, error) pattern to Result data, err := fetchData() result := mur.From(data, err)
These functions bridge the gap between traditional Go patterns and monadic types, making it easier to adopt mu in existing codebases.
Replace nested error checking with declarative chains:
// Before: nested error checking func processOrder(orderID int) (string, error) { order, err := fetchOrder(orderID) if err != nil { return "", fmt.Errorf("fetch failed: %w", err) } payment, err := processPayment(order) if err != nil { return "", fmt.Errorf("payment failed: %w", err) } return "success", nil } // After: declarative chain func processOrder(orderID int) (string, error) { return mu.Ok(orderID). FlatMap(fetchOrderResult). FlatMap(processPaymentResult). Map(func(_ Payment) string { return "success" }). Unpack() }
Handle complex optional value operations:
// Extract nested optional values result := user.GetProfile(). FlatMap(func(p Profile) mu.Option[string] { return p.GetEmail() }). Map(func(email string) string { return strings.ToLower(email) }). OrElse("no-email@example.com")
Use Option[T] when:
- A value might legitimately be absent
- You want to avoid nil pointer errors
- Working with optional configuration or user input
Use Result[T] when:
- Operations can fail with meaningful errors
- You want to chain multiple failable operations
- Converting from existing
(T, error)patterns
Use Either[L, R] when:
- You have exactly two distinct value types
- Neither type represents an "error" state
- Implementing state machines or parsers
For library authors:
// Public APIs should use standard Go patterns func ProcessData(input string) (Output, error) { // Use mu internally for complex logic result := mu.Ok(input). FlatMap(validate). FlatMap(transform) // Convert back to standard Go at API boundary return result.Unpack() }
For application code:
// Use mu types throughout your application logic func processUserData(userData mu.Option[UserInput]) mu.Result[ProcessedData] { return muo.MapTo(userData, processInput). ToResult(errors.New("no user data")) }
For detailed API documentation, see the Go Reference.
- github.com/appthrust/mu - Core types and methods
- github.com/appthrust/mu/muo - Option utility functions
- github.com/appthrust/mu/mur - Result utility functions
- github.com/appthrust/mu/mue - Either utility functions
We welcome contributions! Please see our Contributing Guidelines for details.
This project is licensed under the MIT License - see the LICENSE file for details.
This library draws inspiration from functional programming languages and libraries:
- Rust's
OptionandResulttypes - Scala's functional collections
- Swift's optionals
- Existing Go libraries like samber/mo
The design prioritizes Go's philosophy of simplicity and explicitness while bringing the safety and expressiveness of functional programming to Go developers.