mu

package module
v0.0.0-...-f1dd236 Latest Latest
Warning

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

Go to latest
Published: Aug 2, 2025 License: MIT Imports: 0 Imported by: 0

README

mu - Functional Programming Primitives for Go

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.

Name Origin and Pronunciation

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.

Why mu?

  • 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

Quick Start

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]()
}

Core Types

Option[T] - Safe Optional Values

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] - Failable Operations

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] - Disjoint Union

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 })

Go Generics Design

Due to Go's type system limitations, this library uses a specific pattern to provide both ergonomic and powerful APIs:

Methods vs Functions

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

Integration with Go Ecosystem

Error Handling Bridge

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()
Converting Go Patterns to mu Types

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.

Advanced Patterns

Chaining Failable Operations

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()
}
Option Chaining

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")

Best Practices

When to Use Each Type

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
API Design Guidelines

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"))
}

Documentation

For detailed API documentation, see the Go Reference.

Key Packages

Contributing

We welcome contributions! Please see our Contributing Guidelines for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Inspiration

This library draws inspiration from functional programming languages and libraries:

  • Rust's Option and Result types
  • 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.

Documentation

Overview

Package mu provides functional programming primitives for Go, including Option, Result, and Either types for safe and expressive error handling and data manipulation.

The package offers three main abstractions:

  • Option: Represents optional values, avoiding nil pointer issues
  • Either: Represents a disjoint union of two possible value types
  • Result: Represents operations that may succeed or fail with an error

Each type comes with a rich set of methods for functional composition, transformation, and safe value extraction. The types implement standard Go interfaces for JSON marshaling, SQL scanning, and string representation.

Example usage:

// Option example
opt := Some(42)
doubled := opt.Map(func(x int) int { return x * 2 })
fmt.Println(doubled.OrZero()) // 84

// Result example
result := Try(func() (int, error) {
	return strconv.Atoi("123")
})
fmt.Println(result.OrZero()) // 123

// Either example
either := Right[string, int](42)
doubled := either.MapRight(func(x int) int { return x * 2 })
fmt.Println(doubled.RightOrPanic()) // 84

Design Patterns and Go Generics Limitations

This library follows a specific design pattern to work around Go's generics limitations while providing both ergonomic and powerful APIs.

## Method vs Function Distinction

Due to Go's language specification, methods cannot have additional type parameters beyond those defined on the receiver type. This means we cannot write:

func (o Option[T]) Map[U any](f func(T) U) Option[U]  // ❌ Not allowed

Instead, methods in this library are endomorphisms (same type in, same type out):

func (o Option[T]) Map(f func(T) T) Option[T]         // ✅ Allowed

For type-changing transformations, we provide standalone functions:

func MapOptionTo[T, U any](o Option[T], f func(T) U) Option[U]  // ✅ Allowed

## Usage Guidelines

Use methods when:

  • Transforming a value while keeping the same type (e.g., `x * 2` for int)
  • Chaining operations fluently: `opt.Map(f1).Filter(p).Map(f2)`

Use standalone functions when:

  • Converting between different types (e.g., int to string)
  • You need the additional type parameter flexibility

Example:

// Method: int → int transformation
opt := Some(42)
doubled := opt.Map(func(x int) int { return x * 2 })

// Function: int → string transformation
opt := Some(42)
str := MapOptionTo(opt, func(x int) string { return fmt.Sprintf("%d", x) })

For more details on this limitation, see: https://github.com/golang/go/issues/49085

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Either

type Either[L any, R any] struct {
	// contains filtered or unexported fields
}

Either represents a value of one of two possible types (a disjoint union). An instance of `Either[L, R]` is either a Left or a Right. This implementation is right-biased: methods such as `Map`, `FlatMap`, and `Get` operate on the Right side by default.

A right-biased approach offers several advantages:

  1. It follows the common convention where Right is the "happy-path" value and Left represents an alternative or error. This makes it natural to chain operations without repetitive branching.
  2. Method signatures stay concise because they focus on the Right type, improving readability.
  3. It allows Either to be treated as a monad, enabling fluent functional composition on the successful value.

Either is still a general-purpose sum type and remains distinct from Result, which is specialized for success/failure semantics.

func Left

func Left[L any, R any](value L) Either[L, R]

Left returns an `Either[L, R]` that contains a Left value.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	either := mu.Left[string, int]("error message")
	fmt.Println("Left(\"error message\"):", either.IsLeft())
	fmt.Println("Left value:", either.LeftOrZero())

}
Output:

Left("error message"): true
Left value: error message
func Right[L any, R any](value R) Either[L, R]

Right returns an `Either[L, R]` that contains a Right value.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	either := mu.Right[string, int](42)
	fmt.Println("Right(42):", either.IsRight())
	fmt.Println("Right value:", either.OrZero())

}
Output:

Right(42): true
Right value: 42

func (Either[L, R]) FlatMap

func (e Either[L, R]) FlatMap(f func(R) Either[L, R]) Either[L, R]

FlatMap returns the original Left if the Either is a Left, otherwise calls the function with the Right value and returns the new Either. For type-changing operations (R→R2), use pkg/github.com/appthrust/mu/mue.FlatMapTo instead. See package docs for details.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// FlatMap with Right value
	right := mu.Right[string, int](42)
	result := right.FlatMap(func(x int) mu.Either[string, int] {
		if x > 40 {
			return mu.Right[string, int](x * 2)
		}
		return mu.Left[string, int]("too small")
	})
	fmt.Println("Right(42).FlatMap(x*2 if >40):", result.OrZero())

	// FlatMap with Left value
	left := mu.Left[string, int]("error")
	result2 := left.FlatMap(func(x int) mu.Either[string, int] {
		return mu.Right[string, int](x * 2)
	})
	fmt.Println("Left(\"error\").FlatMap():", result2.LeftOrZero())

}
Output:

Right(42).FlatMap(x*2 if >40): 84
Left("error").FlatMap(): error

func (Either[L, R]) FlatMapLeft

func (e Either[L, R]) FlatMapLeft(f func(L) Either[L, R]) Either[L, R]

FlatMapLeft returns the original Right if the Either is a Right, otherwise calls the function with the Left value and returns the new Either. For type-changing operations (L→L2), use pkg/github.com/appthrust/mu/mue.FlatMapLeftTo instead. See package docs for details.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// FlatMapLeft with Left value
	left := mu.Left[string, int]("error")
	result := left.FlatMapLeft(func(s string) mu.Either[string, int] {
		return mu.Left[string, int]("processed: " + s)
	})
	fmt.Println("Left(\"error\").FlatMapLeft():", result.LeftOrZero())

	// FlatMapLeft with Right value
	right := mu.Right[string, int](42)
	result2 := right.FlatMapLeft(func(s string) mu.Either[string, int] {
		return mu.Left[string, int]("processed: " + s)
	})
	fmt.Println("Right(42).FlatMapLeft():", result2.OrZero())

}
Output:

Left("error").FlatMapLeft(): processed: error
Right(42).FlatMapLeft(): 42

func (Either[L, R]) Flatten

func (e Either[L, R]) Flatten() Either[L, R]

Flatten converts an right-biased `Either[L, Either[L, R]]` into an `Either[L, R]`. This requires the inner type R to be an `Either[L, R]`.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Note: Flatten works on properly nested Either types through type assertion
	// Create nested Either scenario
	innerRight := mu.Right[string, int](42)
	outerRight := mu.Right[string, mu.Either[string, int]](innerRight)

	result := outerRight.Flatten()
	fmt.Println("Nested Right contains value:", result.IsRight())
	fmt.Println("Value:", result.OrZero())

	// Example with inner Left
	innerLeft := mu.Left[string, int]("inner error")
	outerWithLeft := mu.Right[string, mu.Either[string, int]](innerLeft)
	result2 := outerWithLeft.Flatten()
	fmt.Println("Nested with inner Left:", result2.IsLeft())
	fmt.Printf("Left value:%s\n", result2.LeftOrZero())

	// Example with outer Left
	outerLeft := mu.Left[string, mu.Either[string, int]]("outer error")
	result3 := outerLeft.Flatten()
	fmt.Println("Outer Left flattened:", result3.IsLeft())
	fmt.Println("Left value:", result3.LeftOrZero())

}
Output:

Nested Right contains value: true
Value: { 42 false}
Nested with inner Left: false
Left value:
Outer Left flattened: true
Left value: outer error

func (Either[L, R]) Fold

func (e Either[L, R]) Fold(onLeft func(L) R, onRight func(R) R) R

Fold applies the function f to the contained value of `Either[L, R]`, returning the result of onLeft or onRight depending on the variant. For type-changing transformations (L→U, R→U), use pkg/github.com/appthrust/mu/mue.Fold instead. See package docs for details.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Fold with Left value
	left := mu.Left[string, int]("error")
	result := left.Fold(
		func(s string) int { return len(s) }, // onLeft
		func(x int) int { return x * 2 },     // onRight
	)
	fmt.Println("Left(\"error\").Fold(len, x*2):", result)

	// Fold with Right value
	right := mu.Right[string, int](42)
	result2 := right.Fold(
		func(s string) int { return len(s) }, // onLeft
		func(x int) int { return x * 2 },     // onRight
	)
	fmt.Println("Right(42).Fold(len, x*2):", result2)

}
Output:

Left("error").Fold(len, x*2): 5
Right(42).Fold(len, x*2): 84

func (Either[L, R]) IsLeft

func (e Either[L, R]) IsLeft() bool

IsLeft returns true if the Either is a Left value.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Create a Left value
	leftValue := mu.Left[string, int]("error")
	fmt.Println("Left(\"error\").IsLeft():", leftValue.IsLeft())

	// Create a Right value
	rightValue := mu.Right[string, int](42)
	fmt.Println("Right(42).IsLeft():", rightValue.IsLeft())

}
Output:

Left("error").IsLeft(): true
Right(42).IsLeft(): false

func (Either[L, R]) IsRight

func (e Either[L, R]) IsRight() bool

IsRight returns true if the Either is a Right value.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Create a Left value
	leftValue := mu.Left[string, int]("error")
	fmt.Println("Left(\"error\").IsRight():", leftValue.IsRight())

	// Create a Right value
	rightValue := mu.Right[string, int](42)
	fmt.Println("Right(42).IsRight():", rightValue.IsRight())

}
Output:

Left("error").IsRight(): false
Right(42).IsRight(): true

func (Either[L, R]) Left

func (e Either[L, R]) Left() Option[L]

Left returns the Left value as an `Option[L]`.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Left with Left value
	left := mu.Left[string, int]("error message")
	leftOpt := left.Left()
	fmt.Println("Left(\"error message\").Left():", leftOpt.IsSome())
	fmt.Println("Value:", leftOpt.OrZero())

	// Left with Right value
	right := mu.Right[string, int](42)
	leftOpt2 := right.Left()
	fmt.Println("Right(42).Left():", leftOpt2.IsNone())

}
Output:

Left("error message").Left(): true
Value: error message
Right(42).Left(): true

func (Either[L, R]) LeftOrElse

func (e Either[L, R]) LeftOrElse(fallback L) L

LeftOrElse returns the contained Left value or the provided fallback value if the Either is a Right.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// LeftOrElse with Left value
	left := mu.Left[string, int]("error")
	result := left.LeftOrElse("fallback")
	fmt.Println("Left(\"error\").LeftOrElse(\"fallback\"):", result)

	// LeftOrElse with Right value
	right := mu.Right[string, int](42)
	result2 := right.LeftOrElse("fallback")
	fmt.Println("Right(42).LeftOrElse(\"fallback\"):", result2)

}
Output:

Left("error").LeftOrElse("fallback"): error
Right(42).LeftOrElse("fallback"): fallback

func (Either[L, R]) LeftOrElseF

func (e Either[L, R]) LeftOrElseF(fallback func() L) L

LeftOrElseF returns the contained Left value or computes it from a closure if the Either is a Right. The closure is lazily evaluated.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// LeftOrElseF with Left value
	left := mu.Left[string, int]("error")
	result := left.LeftOrElseF(func() string { return "fallback" })
	fmt.Println("Left(\"error\").LeftOrElseF(func):", result)

	// LeftOrElseF with Right value
	right := mu.Right[string, int](42)
	result2 := right.LeftOrElseF(func() string { return "fallback" })
	fmt.Println("Right(42).LeftOrElseF(func):", result2)

}
Output:

Left("error").LeftOrElseF(func): error
Right(42).LeftOrElseF(func): fallback

func (Either[L, R]) LeftOrPanic

func (e Either[L, R]) LeftOrPanic() L

LeftOrPanic returns the contained Left value. It panics if the Either is a Right.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// LeftOrPanic with Left value (safe)
	left := mu.Left[string, int]("error")
	result := left.LeftOrPanic()
	fmt.Println("Left(\"error\").LeftOrPanic():", result)

	// LeftOrPanic with Right would panic, so we show safe usage
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Right.LeftOrPanic() panicked:", r)
		}
	}()
	right := mu.Right[string, int](42)
	_ = right.LeftOrPanic() // This will panic

}
Output:

Left("error").LeftOrPanic(): error
Right.LeftOrPanic() panicked: called LeftOrPanic() on Right

func (Either[L, R]) LeftOrZero

func (e Either[L, R]) LeftOrZero() L

LeftOrZero returns the contained Left value or the zero value of type L if the Either is a Right.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// LeftOrZero with Left value
	left := mu.Left[string, int]("error")
	result := left.LeftOrZero()
	fmt.Println("Left(\"error\").LeftOrZero():", result)

	// LeftOrZero with Right value
	right := mu.Right[string, int](42)
	result2 := right.LeftOrZero()
	fmt.Println("Right(42).LeftOrZero():", result2)

}
Output:

Left("error").LeftOrZero(): error
Right(42).LeftOrZero():

func (Either[L, R]) Map

func (e Either[L, R]) Map(f func(R) R) Either[L, R]

Map applies a function to a Right value, leaving a Left value untouched. For type-changing transformations (R→R2), use pkg/github.com/appthrust/mu/mue.MapTo instead. See package docs for details.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Map with Right value
	right := mu.Right[string, int](42)
	mapped := right.Map(func(x int) int { return x * 2 })
	fmt.Println("Right(42).Map(x*2):", mapped.OrZero())

	// Map with Left value
	left := mu.Left[string, int]("error")
	mapped2 := left.Map(func(x int) int { return x * 2 })
	fmt.Println("Left(\"error\").Map(x*2):", mapped2.LeftOrZero())

}
Output:

Right(42).Map(x*2): 84
Left("error").Map(x*2): error

func (Either[L, R]) MapLeft

func (e Either[L, R]) MapLeft(f func(L) L) Either[L, R]

MapLeft applies a function to a Left value, leaving a Right value untouched. For type-changing transformations (L→L2), use pkg/github.com/appthrust/mu/mue.MapLeftTo instead. See package docs for details.

Example
package main

import (
	"fmt"
	"strings"

	"github.com/appthrust/mu"
)

func main() {
	// MapLeft with Left value
	left := mu.Left[string, int]("error")
	mapped := left.MapLeft(func(s string) string { return strings.ToUpper(s) })
	fmt.Println("Left(\"error\").MapLeft(upper):", mapped.LeftOrZero())

	// MapLeft with Right value
	right := mu.Right[string, int](42)
	mapped2 := right.MapLeft(func(s string) string { return strings.ToUpper(s) })
	fmt.Println("Right(42).MapLeft(upper):", mapped2.OrZero())

}
Output:

Left("error").MapLeft(upper): ERROR
Right(42).MapLeft(upper): 42

func (Either[L, R]) OrElse

func (e Either[L, R]) OrElse(fallback R) R

OrElse returns the contained Right value or the provided fallback value if the Either is a Left.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrElse with Right value
	right := mu.Right[string, int](42)
	result := right.OrElse(100)
	fmt.Println("Right(42).OrElse(100):", result)

	// OrElse with Left value
	left := mu.Left[string, int]("error")
	result2 := left.OrElse(100)
	fmt.Println("Left(\"error\").OrElse(100):", result2)

}
Output:

Right(42).OrElse(100): 42
Left("error").OrElse(100): 100

func (Either[L, R]) OrElseF

func (e Either[L, R]) OrElseF(fallback func() R) R

OrElseF returns the contained Right value or computes it from a closure if the Either is a Left. The closure is lazily evaluated.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrElseF with Right value
	right := mu.Right[string, int](42)
	result := right.OrElseF(func() int { return 100 })
	fmt.Println("Right(42).OrElseF(func):", result)

	// OrElseF with Left value
	left := mu.Left[string, int]("error")
	result2 := left.OrElseF(func() int { return 100 })
	fmt.Println("Left(\"error\").OrElseF(func):", result2)

}
Output:

Right(42).OrElseF(func): 42
Left("error").OrElseF(func): 100

func (Either[L, R]) OrPanic

func (e Either[L, R]) OrPanic() R

OrPanic returns the contained Right value. It panics if the Either is a Left.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrPanic with Right value (safe)
	right := mu.Right[string, int](42)
	result := right.OrPanic()
	fmt.Println("Right(42).OrPanic():", result)

	// OrPanic with Left would panic, so we show safe usage
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Left.OrPanic() panicked:", r)
		}
	}()
	left := mu.Left[string, int]("error")
	_ = left.OrPanic() // This will panic

}
Output:

Right(42).OrPanic(): 42
Left.OrPanic() panicked: called OrPanic() on Left

func (Either[L, R]) OrZero

func (e Either[L, R]) OrZero() R

OrZero returns the contained Right value or the zero value of type R if the Either is a Left.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrZero with Right value
	right := mu.Right[string, int](42)
	result := right.OrZero()
	fmt.Println("Right(42).OrZero():", result)

	// OrZero with Left value
	left := mu.Left[string, int]("error")
	result2 := left.OrZero()
	fmt.Println("Left(\"error\").OrZero():", result2)

}
Output:

Right(42).OrZero(): 42
Left("error").OrZero(): 0

func (Either[L, R]) Right

func (e Either[L, R]) Right() Option[R]

Right returns the Right value as an `Option[R]`.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Right with Right value
	right := mu.Right[string, int](42)
	rightOpt := right.Right()
	fmt.Println("Right(42).Right():", rightOpt.IsSome())
	fmt.Println("Value:", rightOpt.OrZero())

	// Right with Left value
	left := mu.Left[string, int]("error")
	rightOpt2 := left.Right()
	fmt.Println("Left(\"error\").Right():", rightOpt2.IsNone())

}
Output:

Right(42).Right(): true
Value: 42
Left("error").Right(): true

func (Either[L, R]) Swap

func (e Either[L, R]) Swap() Either[R, L]

Swap transforms an `Either[L, R]` into an `Either[R, L]`.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Swap Left to Right
	left := mu.Left[string, int]("error message")
	swapped := left.Swap()
	fmt.Println("Left(\"error message\").Swap() is Right:", swapped.IsRight())
	fmt.Println("Right value:", swapped.OrZero())

	// Swap Right to Left
	right := mu.Right[string, int](42)
	swapped2 := right.Swap()
	fmt.Println("Right(42).Swap() is Left:", swapped2.IsLeft())
	fmt.Println("Left value:", swapped2.LeftOrZero())

	// Demonstrate type transformation: Either[string, int] -> Either[int, string]
	original := mu.Left[string, int]("hello")
	swapped3 := original.Swap() // Now Either[int, string]
	fmt.Println("Original Left(\"hello\") becomes Right:", swapped3.IsRight())
	fmt.Println("New Right value:", swapped3.OrZero())

}
Output:

Left("error message").Swap() is Right: true
Right value: error message
Right(42).Swap() is Left: true
Left value: 42
Original Left("hello") becomes Right: true
New Right value: hello

func (Either[L, R]) Tap

func (e Either[L, R]) Tap(f func(R)) Either[L, R]

Tap calls the given function with the contained Right value if the Either is a Right. It is used for side-effects and returns the original Either.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Tap with Right value (executes side effect)
	right := mu.Right[string, int](42)
	result := right.Tap(func(x int) {
		fmt.Println("Processing right value:", x)
	})
	fmt.Println("Right.Tap() returns same value:", result.OrZero())

	// Tap with Left value (no side effect)
	left := mu.Left[string, int]("error")
	result2 := left.Tap(func(_ int) {
		fmt.Println("This won't be called")
	})
	fmt.Println("Left.Tap() returns same value:", result2.LeftOrZero())

}
Output:

Processing right value: 42
Right.Tap() returns same value: 42
Left.Tap() returns same value: error

func (Either[L, R]) TapLeft

func (e Either[L, R]) TapLeft(f func(L)) Either[L, R]

TapLeft calls the given function with the contained Left value if the Either is a Left. It is used for side-effects and returns the original Either.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// TapLeft with Left value (executes side effect)
	left := mu.Left[string, int]("error occurred")
	result := left.TapLeft(func(s string) {
		fmt.Println("Logging error:", s)
	})
	fmt.Println("Left.TapLeft() returns same value:", result.LeftOrZero())

	// TapLeft with Right value (no side effect)
	right := mu.Right[string, int](42)
	result2 := right.TapLeft(func(_ string) {
		fmt.Println("This won't be called")
	})
	fmt.Println("Right.TapLeft() returns same value:", result2.OrZero())

}
Output:

Logging error: error occurred
Left.TapLeft() returns same value: error occurred
Right.TapLeft() returns same value: 42

func (Either[L, R]) Unwrap

func (e Either[L, R]) Unwrap() (R, bool)

Unwrap returns the Right value and a boolean indicating its presence.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Unwrap with Right value
	right := mu.Right[string, int](42)
	value, ok := right.Unwrap()
	fmt.Println("Right(42).Unwrap():", value, ok)

	// Unwrap with Left value
	left := mu.Left[string, int]("error")
	value2, ok2 := left.Unwrap()
	fmt.Println("Left(\"error\").Unwrap():", value2, ok2)

	// Pattern matching with Either
	either := mu.Right[string, int](100)
	if rightVal, isRight := either.Unwrap(); isRight {
		fmt.Println("Got right value:", rightVal)
	} else if leftVal, isLeft := either.UnwrapLeft(); isLeft {
		fmt.Println("Got left value:", leftVal)
	}

}
Output:

Right(42).Unwrap(): 42 true
Left("error").Unwrap(): 0 false
Got right value: 100

func (Either[L, R]) UnwrapLeft

func (e Either[L, R]) UnwrapLeft() (L, bool)

UnwrapLeft returns the Left value and a boolean indicating its presence.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// UnwrapLeft with Left value
	left := mu.Left[string, int]("error message")
	value, ok := left.UnwrapLeft()
	fmt.Println("Left(\"error message\").UnwrapLeft():", value, ok)

	// UnwrapLeft with Right value
	right := mu.Right[string, int](42)
	value2, ok2 := right.UnwrapLeft()
	fmt.Println("Right(42).UnwrapLeft():", value2, ok2)

}
Output:

Left("error message").UnwrapLeft(): error message true
Right(42).UnwrapLeft():  false

type Option

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

Option represents an optional value: every `Option[T]` is either Some and contains a value, or None and does not. It provides a type-safe way to handle values that may be absent, avoiding the ambiguity of nil pointers for non-pointer types.

func None

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

None returns an `Option[T]` that represents an absent value.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	value := mu.None[int]()
	fmt.Println("None[int]():", value.IsNone())
	fmt.Println("Value:", value.OrZero())

}
Output:

None[int](): true
Value: 0

func Some

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

Some returns an `Option[T]` that contains the given value.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	value := mu.Some(42)
	fmt.Println("Some(42):", value.IsSome())
	fmt.Println("Value:", value.OrZero())

}
Output:

Some(42): true
Value: 42

func (Option[T]) Filter

func (o Option[T]) Filter(f func(T) bool) Option[T]

Filter returns the option if it is a Some and its value matches the predicate, otherwise returns None.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Filter with Some value that matches predicate
	some42 := mu.Some(42)
	filtered := some42.Filter(func(x int) bool { return x > 40 })
	fmt.Println("Some(42).Filter(>40):", filtered.IsSome())
	fmt.Println("Value:", filtered.OrZero())

	// Filter with Some value that doesn't match predicate
	filtered2 := some42.Filter(func(x int) bool { return x < 40 })
	fmt.Println("Some(42).Filter(<40):", filtered2.IsNone())

	// Filter with None value
	none := mu.None[int]()
	filtered3 := none.Filter(func(x int) bool { return x > 40 })
	fmt.Println("None.Filter(>40):", filtered3.IsNone())

}
Output:

Some(42).Filter(>40): true
Value: 42
Some(42).Filter(<40): true
None.Filter(>40): true

func (Option[T]) FlatMap

func (o Option[T]) FlatMap(f func(T) Option[T]) Option[T]

FlatMap returns None if the option is None, otherwise calls the function with the wrapped value and returns the result. Used for chaining operations. For type-changing operations (T→U), use pkg/github.com/appthrust/mu/muo.FlatMapTo instead. See package docs for details.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// FlatMap with Some value
	some42 := mu.Some(42)
	result := some42.FlatMap(func(x int) mu.Option[int] {
		if x > 40 {
			return mu.Some(x * 2)
		}
		return mu.None[int]()
	})
	fmt.Println("Some(42).FlatMap(x*2 if >40):", result.OrZero())

	// FlatMap with None value
	none := mu.None[int]()
	result2 := none.FlatMap(func(x int) mu.Option[int] {
		return mu.Some(x * 2)
	})
	fmt.Println("None.FlatMap(x*2):", result2.IsNone())

}
Output:

Some(42).FlatMap(x*2 if >40): 84
None.FlatMap(x*2): true

func (Option[T]) Flatten

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

Flatten converts an `Option[Option[T]]` into an `Option[T]`. This requires the inner type T to be an `Option[T]`.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Note: Flatten works on properly nested Option types through type assertion
	// Create a nested Option[Option[int]] scenario
	innerSome := mu.Some(42)
	outerSome := mu.Some(innerSome)

	// For demonstration, we use the Flatten method which attempts type assertion
	result := outerSome.Flatten()
	fmt.Println("Nested Some contains value:", result.IsSome())

	// Example with None inner value
	innerNone := mu.None[int]()
	outerWithNone := mu.Some(innerNone)
	result2 := outerWithNone.Flatten()
	fmt.Println("Nested with inner None:", result2.IsNone())

	// Example with outer None
	outerNone := mu.None[mu.Option[int]]()
	result3 := outerNone.Flatten()
	fmt.Println("Outer None flattened:", result3.IsNone())

}
Output:

Nested Some contains value: true
Nested with inner None: false
Outer None flattened: true

func (Option[T]) Fold

func (o Option[T]) Fold(onSome func(T) T, onNone func() T) T

Fold applies the function f to the contained value of `Option[T]` if it exists, returning the result. If the option is None, it returns the result of the onNone function. For type-changing transformations (T→U), use pkg/github.com/appthrust/mu/muo.Fold instead. See package docs for details.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Fold with Some value
	some42 := mu.Some(42)
	result := some42.Fold(
		func(x int) int { return x * 2 }, // onSome
		func() int { return -1 },         // onNone
	)
	fmt.Println("Some(42).Fold(x*2, -1):", result)

	// Fold with None value
	none := mu.None[int]()
	result2 := none.Fold(
		func(x int) int { return x * 2 }, // onSome
		func() int { return -1 },         // onNone
	)
	fmt.Println("None.Fold(x*2, -1):", result2)

}
Output:

Some(42).Fold(x*2, -1): 84
None.Fold(x*2, -1): -1

func (Option[T]) IsNone

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

IsNone returns true if the option is a None value.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Create a Some value
	someValue := mu.Some(42)
	fmt.Println("Some(42).IsNone():", someValue.IsNone())

	// Create a None value
	noneValue := mu.None[int]()
	fmt.Println("None[int]().IsNone():", noneValue.IsNone())

}
Output:

Some(42).IsNone(): false
None[int]().IsNone(): true

func (Option[T]) IsSome

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

IsSome returns true if the option is a Some value.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Create a Some value
	someValue := mu.Some(42)
	fmt.Println("Some(42).IsSome():", someValue.IsSome())

	// Create a None value
	noneValue := mu.None[int]()
	fmt.Println("None[int]().IsSome():", noneValue.IsSome())

}
Output:

Some(42).IsSome(): true
None[int]().IsSome(): false

func (Option[T]) Map

func (o Option[T]) Map(f func(T) T) Option[T]

Map applies a function to the contained value, transforming T to T. If the option is None, it returns `None[T]`. For type-changing transformations (T→U), use pkg/github.com/appthrust/mu/muo.MapTo instead. See package docs for details.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Map with Some value
	some42 := mu.Some(42)
	mapped := some42.Map(func(x int) int { return x * 2 })
	fmt.Println("Some(42).Map(x*2):", mapped.OrZero())

	// Map with None value
	none := mu.None[int]()
	mapped2 := none.Map(func(x int) int { return x * 2 })
	fmt.Println("None.Map(x*2):", mapped2.IsNone())

}
Output:

Some(42).Map(x*2): 84
None.Map(x*2): true

func (Option[T]) OrElse

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

OrElse returns the contained Some value or the provided fallback value if the option is None.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrElse with Some value
	some42 := mu.Some(42)
	result := some42.OrElse(100)
	fmt.Println("Some(42).OrElse(100):", result)

	// OrElse with None value
	none := mu.None[int]()
	result2 := none.OrElse(100)
	fmt.Println("None.OrElse(100):", result2)

}
Output:

Some(42).OrElse(100): 42
None.OrElse(100): 100

func (Option[T]) OrElseF

func (o Option[T]) OrElseF(fallback func() T) T

OrElseF returns the contained Some value or computes it from a closure if the option is None. The closure is lazily evaluated.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrElseF with Some value
	some42 := mu.Some(42)
	result := some42.OrElseF(func() int { return 100 })
	fmt.Println("Some(42).OrElseF(func):", result)

	// OrElseF with None value
	none := mu.None[int]()
	result2 := none.OrElseF(func() int { return 100 })
	fmt.Println("None.OrElseF(func):", result2)

}
Output:

Some(42).OrElseF(func): 42
None.OrElseF(func): 100

func (Option[T]) OrPanic

func (o Option[T]) OrPanic() T

OrPanic returns the contained Some value. It panics if the option is None. OrPanic should only be used when the presence of a value is guaranteed by the program's logic.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrPanic with Some value (safe)
	some42 := mu.Some(42)
	result := some42.OrPanic()
	fmt.Println("Some(42).OrPanic():", result)

	// OrPanic with None would panic, so we show safe usage
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("None.OrPanic() panicked:", r)
		}
	}()
	none := mu.None[int]()
	_ = none.OrPanic() // This will panic

}
Output:

Some(42).OrPanic(): 42
None.OrPanic() panicked: called OrPanic() on None

func (Option[T]) OrZero

func (o Option[T]) OrZero() T

OrZero returns the contained Some value or the zero value of type T if the option is None.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrZero with Some value
	some42 := mu.Some(42)
	result := some42.OrZero()
	fmt.Println("Some(42).OrZero():", result)

	// OrZero with None value
	none := mu.None[int]()
	result2 := none.OrZero()
	fmt.Println("None.OrZero():", result2)

	// OrZero with string type
	someStr := mu.Some("hello")
	noneStr := mu.None[string]()
	fmt.Println("Some(\"hello\").OrZero():", someStr.OrZero())
	fmt.Println("None[string].OrZero():", noneStr.OrZero())

}
Output:

Some(42).OrZero(): 42
None.OrZero(): 0
Some("hello").OrZero(): hello
None[string].OrZero():

func (Option[T]) Tap

func (o Option[T]) Tap(f func(T)) Option[T]

Tap calls the given function with the contained value if the option is Some. It is used for side-effects and returns the original `Option[T]`.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Tap with Some value (executes side effect)
	some42 := mu.Some(42)
	result := some42.Tap(func(x int) {
		fmt.Println("Side effect: processing value", x)
	})
	fmt.Println("Some(42).Tap() returns same value:", result.OrZero())

	// Tap with None value (no side effect)
	none := mu.None[int]()
	result2 := none.Tap(func(_ int) {
		fmt.Println("This won't be called")
	})
	fmt.Println("None.Tap() returns None:", result2.IsNone())

}
Output:

Side effect: processing value 42
Some(42).Tap() returns same value: 42
None.Tap() returns None: true

func (Option[T]) ToResult

func (o Option[T]) ToResult(err error) Result[T]

ToResult converts the `Option[T]` to a `Result[T]`. If the option is Some, it returns `Ok(value)`. If it is None, it returns `Err(err)` with the provided error.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// ToResult with Some value
	some42 := mu.Some(42)
	result := some42.ToResult(errors.New("not found"))
	fmt.Println("Some(42).ToResult():", result.IsOk())
	fmt.Println("Value:", result.OrZero())

	// ToResult with None value
	none := mu.None[int]()
	result2 := none.ToResult(errors.New("value not found"))
	fmt.Println("None.ToResult():", result2.IsErr())
	fmt.Println("Error:", result2.ErrOrZero().Error())

	// Example use case: converting optional database result to Result
	userOpt := mu.Some("john_doe")
	userResult := userOpt.ToResult(errors.New("user not found"))
	fmt.Println("User found:", userResult.IsOk())
	fmt.Println("Username:", userResult.OrElse("unknown"))

	// Another example with None
	emptyOpt := mu.None[string]()
	emptyResult := emptyOpt.ToResult(errors.New("no data available"))
	fmt.Println("Empty data:", emptyResult.IsErr())
	fmt.Println("Fallback:", emptyResult.OrElse("default"))

}
Output:

Some(42).ToResult(): true
Value: 42
None.ToResult(): true
Error: value not found
User found: true
Username: john_doe
Empty data: true
Fallback: default

func (Option[T]) Unwrap

func (o Option[T]) Unwrap() (T, bool)

Unwrap returns the contained value and a boolean indicating its presence.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Unwrap with Some value
	some42 := mu.Some(42)
	value, ok := some42.Unwrap()
	fmt.Println("Some(42).Unwrap():", value, ok)

	// Unwrap with None value
	none := mu.None[int]()
	value2, ok2 := none.Unwrap()
	fmt.Println("None.Unwrap():", value2, ok2)

	// Practical usage pattern
	if val, present := some42.Unwrap(); present {
		fmt.Println("Value is present:", val)
	} else {
		fmt.Println("Value is absent")
	}

}
Output:

Some(42).Unwrap(): 42 true
None.Unwrap(): 0 false
Value is present: 42

type Result

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

Result represents either success (Ok) or failure (Err). The error type is fixed to Go's built-in `error` interface to ensure maximum compatibility with the standard library and existing Go code.

func Err

func Err[T any](err error) Result[T]

Err returns a `Result[T]` that represents a failed operation with an error.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	result := mu.Err[int](errors.New("something went wrong"))
	fmt.Println("Err(error):", result.IsErr())
	fmt.Println("Error:", result.ErrOrZero().Error())

}
Output:

Err(error): true
Error: something went wrong

func Ok

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

Ok returns a `Result[T]` that represents a successful operation with a value.

Example
package main

import (
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	result := mu.Ok(42)
	fmt.Println("Ok(42):", result.IsOk())
	fmt.Println("Value:", result.OrZero())

}
Output:

Ok(42): true
Value: 42

func Try

func Try[T any](f func() (T, error)) Result[T]

Try executes a function that returns (T, error) and wraps the outcome in a `Result[T]`. If the function returns a nil error, it returns `Ok(value)`. If the function returns a non-nil error, it returns `Err(error)`.

Example
package main

import (
	"fmt"
	"strconv"

	"github.com/appthrust/mu"
)

func main() {
	// Success case
	successResult := mu.Try(func() (int, error) {
		return strconv.Atoi("42")
	})
	fmt.Println("Try success:", successResult.IsOk())
	fmt.Println("Value:", successResult.OrZero())

	// Error case
	errorResult := mu.Try(func() (int, error) {
		return strconv.Atoi("not-a-number")
	})
	fmt.Println("Try error:", errorResult.IsErr())

}
Output:

Try success: true
Value: 42
Try error: true

func Try0

func Try0(f func() error) Result[Unit]

Try0 executes a function that returns only an error and wraps the outcome in a `Result[Unit]`. If the function returns nil, it returns `Ok(UnitValue)`. If the function returns a non-nil error, it returns `Err(error)`. This is useful for operations that can fail but don't produce a meaningful value.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Success case
	successResult := mu.Try0(func() error {
		// Some operation that might fail
		return nil
	})
	fmt.Println("Try0 success:", successResult.IsOk())

	// Error case
	errorResult := mu.Try0(func() error {
		return errors.New("operation failed")
	})
	fmt.Println("Try0 error:", errorResult.IsErr())
	fmt.Println("Error message:", errorResult.ErrOrZero().Error())

}
Output:

Try0 success: true
Try0 error: true
Error message: operation failed

func (Result[T]) ErrOrElse

func (r Result[T]) ErrOrElse(fallback error) error

ErrOrElse returns the contained Err value or the provided fallback value if the result is an Ok.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// ErrOrElse with Err value
	err := mu.Err[int](errors.New("original error"))
	result := err.ErrOrElse(errors.New("fallback error"))
	fmt.Println("Err.ErrOrElse(fallback):", result.Error())

	// ErrOrElse with Ok value
	ok := mu.Ok(42)
	result2 := ok.ErrOrElse(errors.New("fallback error"))
	fmt.Println("Ok(42).ErrOrElse(fallback):", result2.Error())

}
Output:

Err.ErrOrElse(fallback): original error
Ok(42).ErrOrElse(fallback): fallback error

func (Result[T]) ErrOrElseF

func (r Result[T]) ErrOrElseF(fallback func() error) error

ErrOrElseF returns the contained Err value or computes it from a closure if the result is an Ok. The closure is lazily evaluated.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// ErrOrElseF with Err value
	err := mu.Err[int](errors.New("original error"))
	result := err.ErrOrElseF(func() error { return errors.New("fallback error") })
	fmt.Println("Err.ErrOrElseF(func):", result.Error())

	// ErrOrElseF with Ok value
	ok := mu.Ok(42)
	result2 := ok.ErrOrElseF(func() error { return errors.New("fallback error") })
	fmt.Println("Ok(42).ErrOrElseF(func):", result2.Error())

}
Output:

Err.ErrOrElseF(func): original error
Ok(42).ErrOrElseF(func): fallback error

func (Result[T]) ErrOrPanic

func (r Result[T]) ErrOrPanic() error

ErrOrPanic returns the contained Err value. It panics if the result is Ok.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// ErrOrPanic with Err value (safe)
	err := mu.Err[int](errors.New("something went wrong"))
	result := err.ErrOrPanic()
	fmt.Println("Err.ErrOrPanic():", result.Error())

	// ErrOrPanic with Ok would panic, so we show safe usage
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Ok.ErrOrPanic() panicked:", r)
		}
	}()
	ok := mu.Ok(42)
	_ = ok.ErrOrPanic() // This will panic

}
Output:

Err.ErrOrPanic(): something went wrong
Ok.ErrOrPanic() panicked: called ErrOrPanic() on Ok

func (Result[T]) ErrOrZero

func (r Result[T]) ErrOrZero() error

ErrOrZero returns the contained error if the result is an Err, otherwise nil.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// ErrOrZero with Err value
	err := mu.Err[int](errors.New("something went wrong"))
	result := err.ErrOrZero()
	fmt.Println("Err.ErrOrZero():", result.Error())

	// ErrOrZero with Ok value
	ok := mu.Ok(42)
	result2 := ok.ErrOrZero()
	fmt.Println("Ok(42).ErrOrZero():", result2 == nil)

}
Output:

Err.ErrOrZero(): something went wrong
Ok(42).ErrOrZero(): true

func (Result[T]) Error

func (r Result[T]) Error() Option[error]

Error returns the error as an `Option[error]`.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Error with Err
	err := mu.Err[int](errors.New("something went wrong"))
	errorOpt := err.Error()
	fmt.Println("Err.Error():", errorOpt.IsSome())
	fmt.Println("Error message:", errorOpt.OrZero().Error())

	// Error with Ok
	ok := mu.Ok(42)
	errorOpt2 := ok.Error()
	fmt.Println("Ok(42).Error():", errorOpt2.IsNone())

}
Output:

Err.Error(): true
Error message: something went wrong
Ok(42).Error(): true

func (Result[T]) FlatMap

func (r Result[T]) FlatMap(f func(T) Result[T]) Result[T]

FlatMap returns the original Err if the result is an Err, otherwise calls the function with the Ok value and returns the new Result[T]. For type-changing operations (T→U), use pkg/github.com/appthrust/mu/mur.FlatMapTo instead. See package docs for details.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// FlatMap with Ok value
	ok := mu.Ok(42)
	result := ok.FlatMap(func(x int) mu.Result[int] {
		if x > 40 {
			return mu.Ok(x * 2)
		}
		return mu.Err[int](errors.New("too small"))
	})
	fmt.Println("Ok(42).FlatMap(x*2 if >40):", result.OrZero())

	// FlatMap with Err value
	err := mu.Err[int](errors.New("initial error"))
	result2 := err.FlatMap(func(x int) mu.Result[int] {
		return mu.Ok(x * 2)
	})
	fmt.Println("Err.FlatMap():", result2.ErrOrZero().Error())

}
Output:

Ok(42).FlatMap(x*2 if >40): 84
Err.FlatMap(): initial error

func (Result[T]) FlatMapErr

func (r Result[T]) FlatMapErr(f func(error) Result[T]) Result[T]

FlatMapErr applies a function to a contained Err value, returning a new Result. Used for error recovery operations. For type-changing operations (T→U), use pkg/github.com/appthrust/mu/mur.FlatMapErrTo instead. See package docs for details.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// FlatMapErr with Err value
	err := mu.Err[int](errors.New("connection failed"))
	result := err.FlatMapErr(func(e error) mu.Result[int] {
		if e.Error() == "connection failed" {
			return mu.Ok(42) // Recover from specific error
		}
		return mu.Err[int](e)
	})
	fmt.Println("Err(\"connection failed\").FlatMapErr():", result.OrZero())

	// FlatMapErr with Ok value
	ok := mu.Ok(42)
	result2 := ok.FlatMapErr(func(_ error) mu.Result[int] {
		return mu.Ok(0)
	})
	fmt.Println("Ok(42).FlatMapErr():", result2.OrZero())

}
Output:

Err("connection failed").FlatMapErr(): 42
Ok(42).FlatMapErr(): 42

func (Result[T]) Flatten

func (r Result[T]) Flatten() Result[T]

Flatten converts an `Result[Result[T]]` into an `Result[T]`. This requires the inner type T to be an `Result[T]`.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Note: Flatten works on properly nested Result types through type assertion
	// Create nested Result scenario
	innerOk := mu.Ok(42)
	outerOk := mu.Ok(innerOk)

	result := outerOk.Flatten()
	fmt.Println("Nested Ok contains value:", result.IsOk())
	fmt.Println("Value:", result.OrZero())

	// Example with inner Err
	innerErr := mu.Err[int](errors.New("inner error"))
	outerWithErr := mu.Ok(innerErr)
	result2 := outerWithErr.Flatten()
	fmt.Println("Nested with inner Err:", result2.IsErr())
	if result2.IsErr() {
		fmt.Println("Error:", result2.ErrOrZero().Error())
	}

	// Example with outer Err
	outerErr := mu.Err[mu.Result[int]](errors.New("outer error"))
	result3 := outerErr.Flatten()
	fmt.Println("Outer Err flattened:", result3.IsErr())
	if result3.IsErr() {
		fmt.Println("Error:", result3.ErrOrZero().Error())
	}

}
Output:

Nested Ok contains value: true
Value: {42 <nil>}
Nested with inner Err: false
Outer Err flattened: true
Error: outer error

func (Result[T]) Fold

func (r Result[T]) Fold(onOk func(T) T, onErr func(error) T) T

Fold applies the function f to the contained value of Result[T] if it is Ok, returning the result. If the result is Err, it returns the result of the onErr function. For type-changing transformations (T→U), use mur.Fold instead. See package docs for details.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Fold with Ok value
	ok := mu.Ok(42)
	result := ok.Fold(
		func(x int) int { return x * 2 },            // onOk
		func(e error) int { return len(e.Error()) }, // onErr
	)
	fmt.Println("Ok(42).Fold(x*2, len(err)):", result)

	// Fold with Err value
	err := mu.Err[int](errors.New("error"))
	result2 := err.Fold(
		func(x int) int { return x * 2 },            // onOk
		func(e error) int { return len(e.Error()) }, // onErr
	)
	fmt.Println("Err(\"error\").Fold(x*2, len(err)):", result2)

}
Output:

Ok(42).Fold(x*2, len(err)): 84
Err("error").Fold(x*2, len(err)): 5

func (Result[T]) IsErr

func (r Result[T]) IsErr() bool

IsErr returns true if the result is Err.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Create an Ok value
	okValue := mu.Ok(42)
	fmt.Println("Ok(42).IsErr():", okValue.IsErr())

	// Create an Err value
	errValue := mu.Err[int](errors.New("something went wrong"))
	fmt.Println("Err(error).IsErr():", errValue.IsErr())

}
Output:

Ok(42).IsErr(): false
Err(error).IsErr(): true

func (Result[T]) IsOk

func (r Result[T]) IsOk() bool

IsOk returns true if the result is Ok.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Create an Ok value
	okValue := mu.Ok(42)
	fmt.Println("Ok(42).IsOk():", okValue.IsOk())

	// Create an Err value
	errValue := mu.Err[int](errors.New("something went wrong"))
	fmt.Println("Err(error).IsOk():", errValue.IsOk())

}
Output:

Ok(42).IsOk(): true
Err(error).IsOk(): false

func (Result[T]) Map

func (r Result[T]) Map(f func(T) T) Result[T]

Map applies a function to a contained Ok value, leaving an Err value untouched. For type-changing transformations (T→U), use pkg/github.com/appthrust/mu/mur.MapTo instead. See package docs for details.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Map with Ok value
	ok := mu.Ok(42)
	mapped := ok.Map(func(x int) int { return x * 2 })
	fmt.Println("Ok(42).Map(x*2):", mapped.OrZero())

	// Map with Err value
	err := mu.Err[int](errors.New("error"))
	mapped2 := err.Map(func(x int) int { return x * 2 })
	fmt.Println("Err.Map(x*2):", mapped2.ErrOrZero().Error())

}
Output:

Ok(42).Map(x*2): 84
Err.Map(x*2): error

func (Result[T]) MapErr

func (r Result[T]) MapErr(f func(error) error) Result[T]

MapErr applies a function to a contained Err value, leaving an Ok value untouched.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// MapErr with Err value
	err := mu.Err[int](errors.New("connection failed"))
	mapped := err.MapErr(func(e error) error {
		return errors.New("wrapped: " + e.Error())
	})
	fmt.Println("Err.MapErr(wrap):", mapped.ErrOrZero().Error())

	// MapErr with Ok value
	ok := mu.Ok(42)
	mapped2 := ok.MapErr(func(e error) error {
		return errors.New("wrapped: " + e.Error())
	})
	fmt.Println("Ok(42).MapErr(wrap):", mapped2.OrZero())

}
Output:

Err.MapErr(wrap): wrapped: connection failed
Ok(42).MapErr(wrap): 42

func (Result[T]) OrElse

func (r Result[T]) OrElse(fallback T) T

OrElse returns the contained Ok value or the provided fallback value if the result is an Err.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrElse with Ok value
	ok := mu.Ok(42)
	result := ok.OrElse(100)
	fmt.Println("Ok(42).OrElse(100):", result)

	// OrElse with Err value
	err := mu.Err[int](errors.New("error"))
	result2 := err.OrElse(100)
	fmt.Println("Err.OrElse(100):", result2)

}
Output:

Ok(42).OrElse(100): 42
Err.OrElse(100): 100

func (Result[T]) OrElseF

func (r Result[T]) OrElseF(fallback func() T) T

OrElseF returns the contained Ok value or computes it from a closure if the result is an Err. The closure is lazily evaluated.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrElseF with Ok value
	ok := mu.Ok(42)
	result := ok.OrElseF(func() int { return 100 })
	fmt.Println("Ok(42).OrElseF(func):", result)

	// OrElseF with Err value
	err := mu.Err[int](errors.New("error"))
	result2 := err.OrElseF(func() int { return 100 })
	fmt.Println("Err.OrElseF(func):", result2)

}
Output:

Ok(42).OrElseF(func): 42
Err.OrElseF(func): 100

func (Result[T]) OrPanic

func (r Result[T]) OrPanic() T

OrPanic returns the contained Ok value. It panics if the result is an Err. This should be used only when an Ok value is guaranteed.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrPanic with Ok value (safe)
	ok := mu.Ok(42)
	result := ok.OrPanic()
	fmt.Println("Ok(42).OrPanic():", result)

	// OrPanic with Err would panic, so we show safe usage
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Err.OrPanic() panicked:", r)
		}
	}()
	err := mu.Err[int](errors.New("error"))
	_ = err.OrPanic() // This will panic

}
Output:

Ok(42).OrPanic(): 42
Err.OrPanic() panicked: called OrPanic() on Err

func (Result[T]) OrZero

func (r Result[T]) OrZero() T

OrZero returns the contained Ok value or the zero value of type T if the result is an Err.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// OrZero with Ok value
	ok := mu.Ok(42)
	result := ok.OrZero()
	fmt.Println("Ok(42).OrZero():", result)

	// OrZero with Err value
	err := mu.Err[int](errors.New("error"))
	result2 := err.OrZero()
	fmt.Println("Err.OrZero():", result2)

	// OrZero with string type
	okStr := mu.Ok("hello")
	errStr := mu.Err[string](errors.New("error"))
	fmt.Println("Ok(\"hello\").OrZero():", okStr.OrZero())
	fmt.Println("Err[string].OrZero():", errStr.OrZero())

}
Output:

Ok(42).OrZero(): 42
Err.OrZero(): 0
Ok("hello").OrZero(): hello
Err[string].OrZero():

func (Result[T]) Recover

func (r Result[T]) Recover(f func(error) T) Result[T]

Recover maps an Err to an Ok value by applying a recovery function to the error. An Ok value is returned unchanged.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Recover from Err value
	err := mu.Err[int](errors.New("connection failed"))
	recovered := err.Recover(func(e error) int {
		// Provide a default value when there's an error
		if e.Error() == "connection failed" {
			return 42 // Default value for connection errors
		}
		return 0
	})
	fmt.Println("Err.Recover():", recovered.OrZero())

	// Recover with Ok value (no change)
	ok := mu.Ok(100)
	recovered2 := ok.Recover(func(_ error) int {
		return 42
	})
	fmt.Println("Ok(100).Recover():", recovered2.OrZero())

}
Output:

Err.Recover(): 42
Ok(100).Recover(): 100

func (Result[T]) RecoverF

func (r Result[T]) RecoverF(f func(error) Result[T]) Result[T]

RecoverF maps an Err to a new `Result[T]` by applying a recovery function. This allows for fallback operations that may themselves fail.

Example
package main

import (
	"errors"
	"fmt"
	"strconv"

	"github.com/appthrust/mu"
)

func main() {
	// RecoverF from Err value with fallback operation that might also fail
	err := mu.Err[int](errors.New("primary operation failed"))
	recovered := err.RecoverF(func(_ error) mu.Result[int] {
		// Try alternative operation
		return mu.Try(func() (int, error) {
			return strconv.Atoi("42") // Fallback: parse from string
		})
	})
	fmt.Println("Err.RecoverF() success:", recovered.IsOk())
	fmt.Println("Value:", recovered.OrZero())

	// RecoverF with fallback that also fails
	err2 := mu.Err[int](errors.New("primary operation failed"))
	recovered2 := err2.RecoverF(func(_ error) mu.Result[int] {
		// Alternative operation that also fails
		return mu.Try(func() (int, error) {
			return strconv.Atoi("not-a-number")
		})
	})
	fmt.Println("Err.RecoverF() failed:", recovered2.IsErr())

	// RecoverF with Ok value (no change)
	ok := mu.Ok(100)
	recovered3 := ok.RecoverF(func(_ error) mu.Result[int] {
		return mu.Ok(42)
	})
	fmt.Println("Ok(100).RecoverF():", recovered3.OrZero())

}
Output:

Err.RecoverF() success: true
Value: 42
Err.RecoverF() failed: true
Ok(100).RecoverF(): 100

func (Result[T]) Tap

func (r Result[T]) Tap(f func(T)) Result[T]

Tap calls the given function with the contained value if the result is Ok. It is used for side-effects and returns the original Result.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Tap with Ok value (executes side effect)
	ok := mu.Ok(42)
	result := ok.Tap(func(x int) {
		fmt.Println("Processing successful result:", x)
	})
	fmt.Println("Ok.Tap() returns same value:", result.OrZero())

	// Tap with Err value (no side effect)
	err := mu.Err[int](errors.New("operation failed"))
	result2 := err.Tap(func(_ int) {
		fmt.Println("This won't be called")
	})
	fmt.Println("Err.Tap() returns same error:", result2.ErrOrZero().Error())

}
Output:

Processing successful result: 42
Ok.Tap() returns same value: 42
Err.Tap() returns same error: operation failed

func (Result[T]) TapErr

func (r Result[T]) TapErr(f func(error)) Result[T]

TapErr calls the given function with the contained error if the result is Err. It is used for side-effects (like logging) and returns the original Result.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// TapErr with Err value (executes side effect)
	err := mu.Err[int](errors.New("operation failed"))
	result := err.TapErr(func(e error) {
		fmt.Println("Logging error:", e.Error())
	})
	fmt.Println("Err.TapErr() returns same error:", result.ErrOrZero().Error())

	// TapErr with Ok value (no side effect)
	ok := mu.Ok(42)
	result2 := ok.TapErr(func(_ error) {
		fmt.Println("This won't be called")
	})
	fmt.Println("Ok.TapErr() returns same value:", result2.OrZero())

}
Output:

Logging error: operation failed
Err.TapErr() returns same error: operation failed
Ok.TapErr() returns same value: 42

func (Result[T]) Unpack

func (r Result[T]) Unpack() (T, error)

Unpack converts Result[T] back to Go's standard (value, error) pattern. If the Result is Ok, returns (value, nil). If the Result is Err, returns (zero_value, error).

Example

ExampleResult_Unpack demonstrates converting Result back to Go's (value, error) pattern

package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Ok result unpacks to (value, nil)
	okResult := mu.Ok(42)
	value, err := okResult.Unpack()
	fmt.Printf("Ok result: value=%d, err=%v\n", value, err)

	// Err result unpacks to (zero_value, error)
	errResult := mu.Err[int](errors.New("something went wrong"))
	value, err = errResult.Unpack()
	fmt.Printf("Err result: value=%d, err=%v\n", value, err)

}
Output:

Ok result: value=42, err=<nil>
Err result: value=0, err=something went wrong
Example (BridgeToLegacyCode)

ExampleResult_Unpack_bridgeToLegacyCode shows bridging mu.Result to legacy Go code

package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Legacy function that expects (value, error)
	processData := func(data string, err error) {
		if err != nil {
			fmt.Printf("Processing failed: %v\n", err)
			return
		}
		fmt.Printf("Processing: %s\n", data)
	}

	// Modern function that returns Result
	fetchData := func(id int) mu.Result[string] {
		if id <= 0 {
			return mu.Err[string](errors.New("invalid ID"))
		}
		return mu.Ok(fmt.Sprintf("data-%d", id))
	}

	// Bridge between modern and legacy code
	data, err := fetchData(123).Unpack()
	processData(data, err)

	data, err = fetchData(-1).Unpack()
	processData(data, err)

}
Output:

Processing: data-123
Processing failed: invalid ID
Example (WithFunctions)

ExampleResult_Unpack_withFunctions demonstrates using Unpack with functions

package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Function that returns Result
	divide := func(a, b int) mu.Result[int] {
		if b == 0 {
			return mu.Err[int](errors.New("division by zero"))
		}
		return mu.Ok(a / b)
	}

	// Convert Result back to Go pattern for traditional error handling
	handleDivision := func(a, b int) {
		value, err := divide(a, b).Unpack()
		if err != nil {
			fmt.Printf("Error: %v\n", err)
			return
		}
		fmt.Printf("Result: %d\n", value)
	}

	handleDivision(10, 2)
	handleDivision(10, 0)

}
Output:

Result: 5
Error: division by zero

func (Result[T]) Unwrap

func (r Result[T]) Unwrap() (T, bool)

Unwrap returns the value and a boolean indicating its presence.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Unwrap with Ok value
	ok := mu.Ok(42)
	value, isOk := ok.Unwrap()
	fmt.Println("Ok(42).Unwrap():", value, isOk)

	// Unwrap with Err value
	err := mu.Err[int](errors.New("operation failed"))
	value2, isOk2 := err.Unwrap()
	fmt.Println("Err.Unwrap():", value2, isOk2)

	// Error handling pattern
	result := mu.Ok(200)
	if val, success := result.Unwrap(); success {
		fmt.Println("Operation succeeded with value:", val)
	} else {
		fmt.Println("Operation failed")
	}

}
Output:

Ok(42).Unwrap(): 42 true
Err.Unwrap(): 0 false
Operation succeeded with value: 200

func (Result[T]) UnwrapErr

func (r Result[T]) UnwrapErr() (error, bool)

UnwrapErr returns the error and a boolean indicating its presence.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// UnwrapErr with Err value
	err := mu.Err[int](errors.New("something went wrong"))
	errValue, hasErr := err.UnwrapErr()
	fmt.Println("Err.UnwrapErr():", errValue.Error(), hasErr)

	// UnwrapErr with Ok value
	ok := mu.Ok(42)
	errValue2, hasErr2 := ok.UnwrapErr()
	fmt.Println("Ok(42).UnwrapErr():", errValue2 == nil, hasErr2)

	// Error handling with UnwrapErr
	result := mu.Err[string](errors.New("network timeout"))
	if e, hasError := result.UnwrapErr(); hasError {
		fmt.Println("Error occurred:", e.Error())
	} else {
		fmt.Println("No error")
	}

}
Output:

Err.UnwrapErr(): something went wrong true
Ok(42).UnwrapErr(): true false
Error occurred: network timeout

func (Result[T]) Value

func (r Result[T]) Value() Option[T]

Value returns the value as an `Option[T]`.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/appthrust/mu"
)

func main() {
	// Value with Ok
	ok := mu.Ok(42)
	valueOpt := ok.Value()
	fmt.Println("Ok(42).Value():", valueOpt.IsSome())
	fmt.Println("Value:", valueOpt.OrZero())

	// Value with Err
	err := mu.Err[int](errors.New("something went wrong"))
	valueOpt2 := err.Value()
	fmt.Println("Err.Value():", valueOpt2.IsNone())

}
Output:

Ok(42).Value(): true
Value: 42
Err.Value(): true

type Unit

type Unit struct{}

Unit represents a unit type, similar to void in other languages. It carries no information and is used when a type parameter is required but no meaningful value needs to be carried.

var UnitValue Unit

UnitValue is the singleton instance of Unit type. Use this instead of creating new `Unit{}` instances.

Directories

Path Synopsis
Package mue provides type-changing utility functions for `Either[L,R]`.
Package mue provides type-changing utility functions for `Either[L,R]`.
Package muo provides type-changing utility functions for `Option[T]`.
Package muo provides type-changing utility functions for `Option[T]`.
Package mur provides type-changing utility functions for `Result[T]`.
Package mur provides type-changing utility functions for `Result[T]`.

Jump to

Keyboard shortcuts

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