maybe

package module
v0.1.9 Latest Latest
Warning

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

Go to latest
Published: Feb 26, 2026 License: MIT Imports: 3 Imported by: 6

README

Go Maybe

A generic Maybe/Option type implementation for Go, providing a safe way to handle potentially absent values without using pointers or nil checks.

Features

  • Type-safe: Uses Go generics to provide compile-time type safety
  • Zero dependencies: Pure Go implementation with no external dependencies
  • Comprehensive API: Includes Map, FlatMap, Filter, and more
  • Type transformations: Helper functions support transforming between different types
  • Safe unwrapping: Multiple ways to extract values with proper error handling
  • JSON v2 support: Optional integration with Go's experimental json/v2 package

JSON v2 Support (Go 1.25+)

When built with GOEXPERIMENT=jsonv2, Maybe seamlessly integrates with encoding/json's omitzero tag:

type Config struct {
    Name    string          `json:"name"`
    Timeout maybe.Maybe[int] `json:"timeout,omitzero"`
}

// Unmarshalling: missing fields become None
json.Unmarshal([]byte(`{"name": "test"}`), &cfg)
cfg.Timeout.IsNone() // true

// Marshalling: None fields are omitted
cfg := Config{Name: "test", Timeout: maybe.None[int]()}
json.Marshal(cfg) // {"name":"test"}

// Present values work normally
cfg.Timeout = maybe.Some(30)
json.Marshal(cfg) // {"name":"test","timeout":30}

Build with: GOEXPERIMENT=jsonv2 go build ./...

Json NULL semantics
  • null on none pointer values are None()
  • null on pointer values are Some(nil)

Installation

go get github.com/zodimo/go-maybe

Usage

Creating Maybe Values
import "github.com/zodimo/go-maybe"

// Create a Some value
value := maybe.Some(42)

// Create a None value
empty := maybe.None[int]()

// Create a new empty Maybe (same as None)
m := maybe.NewMaybe[string]()
Checking Values
m := maybe.Some(42)

if m.IsSome() {
    // Value is present
}

if m.IsNone() {
    // Value is absent
}
Extracting Values
// Safe unwrapping with error
value, err := m.Unwrap()
if err != nil {
    // Handle error
}

// Unsafe unwrapping (panics if None)
value := m.UnwrapUnsafe() // Use with caution!

// UnwrapOr: provide a default value if None
m := maybe.None[int]()
value := m.UnwrapOr(100) // Returns 100
Transforming Values
Methods (Same Type)

The methods Map, FlatMap, and Filter work on the same type:

// Map: transform the value if present (same type)
m := maybe.Some(5)
doubled := m.Map(func(x int) int { return x * 2 })
// doubled is Some(10)

// FlatMap: transform to another Maybe (same type)
m := maybe.Some(5)
result := m.FlatMap(func(x int) maybe.Maybe[int] {
    if x > 0 {
        return maybe.Some(x * 2)
    }
    return maybe.None[int]()
})

// Filter: keep value only if predicate is true
m := maybe.Some(10)
filtered := m.Filter(func(x int) bool { return x > 5 })
// filtered is Some(10)
Helper Functions (Type Transformations)

The standalone helper functions Map, FlatMap, and Filter support transforming between different types:

// Map: transform int to string
m := maybe.Some(42)
result := maybe.Map(m, func(x int) string {
    return fmt.Sprintf("number: %d", x)
})
// result is Maybe[string] with value "number: 42"

// Map: transform string to int
m := maybe.Some("hello")
result := maybe.Map(m, func(s string) int {
    return len(s)
})
// result is Maybe[int] with value 5

// FlatMap: transform int to Maybe[string]
m := maybe.Some(5)
result := maybe.FlatMap(m, func(x int) maybe.Maybe[string] {
    if x > 0 {
        return maybe.Some("positive")
    }
    return maybe.None[string]()
})
// result is Maybe[string] with value "positive"
Providing Default Values
// OrElse: provide a default value
m := maybe.None[int]()
value := m.OrElse(100) // Returns 100

// OrElseGet: compute default value lazily
m := maybe.None[int]()
value := m.OrElseGet(func() int {
    return expensiveComputation()
})

// OrElseError: return an error if None
m := maybe.None[int]()
value, err := m.OrElseError(errors.New("value not found"))
Chaining Operations
result := maybe.Some(5).
    Map(func(x int) int { return x * 2 }).
    Map(func(x int) int { return x + 1 }).
    Filter(func(x int) bool { return x > 10 }).
    OrElse(0)

API Reference

Types
  • Maybe[T]: A generic type that represents either a value (Some) or no value (None)
Functions
  • Some[T any](value T) Maybe[T]: Creates a Maybe with a value
  • None[T any]() Maybe[T]: Creates an empty Maybe
  • NewMaybe[T any]() Maybe[T]: Creates a new empty Maybe (alias for None)
  • Map[T any, R any](m Maybe[T], f func(T) R) Maybe[R]: Transforms a Maybe[T] to Maybe[R] by applying function f if the value is present
  • FlatMap[T any, R any](m Maybe[T], f func(T) Maybe[R]) Maybe[R]: Transforms a Maybe[T] to Maybe[R] by applying function f that returns a Maybe[R] if the value is present
Methods
  • IsSome() bool: Returns true if the Maybe contains a value
  • IsNone() bool: Returns true if the Maybe is empty
  • Unwrap() (T, error): Returns the value and an error (error is non-nil if None)
  • UnwrapUnsafe() T: Returns the value, panics if None
  • UnwrapOr(defaultValue T) T: Returns the value if present, otherwise returns defaultValue
  • Map(f func(T) T) Maybe[T]: Transforms the value if present, returns None otherwise
  • FlatMap(f func(T) Maybe[T]) Maybe[T]: Transforms to another Maybe if present
  • Filter(f func(T) bool) Maybe[T]: Keeps the value only if the predicate returns true
  • OrElse(elseValue T) T: Returns the value if present, otherwise returns elseValue
  • OrElseGet(f func() T) T: Returns the value if present, otherwise calls f() and returns its result
  • OrElseError(err error) (T, error): Returns the value and nil error if present, otherwise returns zero value and err

Examples

Handling Optional Return Values
func divide(a, b int) maybe.Maybe[int] {
    if b == 0 {
        return maybe.None[int]()
    }
    return maybe.Some(a / b)
}

result := divide(10, 2)
value, err := result.Unwrap()
if err != nil {
    log.Println("Division failed")
} else {
    log.Printf("Result: %d", value)
}
Safe Map Access
func getValue(m map[string]int, key string) maybe.Maybe[int] {
    if val, ok := m[key]; ok {
        return maybe.Some(val)
    }
    return maybe.None[int]()
}

m := map[string]int{"foo": 42}
value := getValue(m, "foo").OrElse(0)
Type Transformations with Helper Functions
// Transform a number to a formatted string
num := maybe.Some(42)
formatted := maybe.Map(num, func(x int) string {
    return fmt.Sprintf("Value: %d", x)
})
// formatted is Maybe[string] with value "Value: 42"

// Parse a string to an integer with validation
str := maybe.Some("123")
parsed := maybe.FlatMap(str, func(s string) maybe.Maybe[int] {
    if val, err := strconv.Atoi(s); err == nil {
        return maybe.Some(val)
    }
    return maybe.None[int]()
})
// parsed is Maybe[int] with value 123
Chaining Transformations
// Using methods (same type)
result := maybe.Some("hello").
    Map(func(s string) string { return strings.ToUpper(s) }).
    Map(func(s string) string { return s + " WORLD" }).
    Filter(func(s string) bool { return len(s) > 5 }).
    OrElse("default")

// Using helper functions (type transformations)
m := maybe.Some(42)
strMaybe := maybe.Map(m, func(x int) string { return fmt.Sprintf("%d", x) })
intMaybe := maybe.FlatMap(strMaybe, func(s string) maybe.Maybe[int] {
    if len(s) > 0 {
        return maybe.Some(len(s))
    }
    return maybe.None[int]()
})
result := intMaybe.OrElse(0)

License

See LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Match added in v0.1.4

func Match[T, U any](m Maybe[T], onSome func(T) U, onNone func() U) U

Types

type Maybe

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

func FlatMap added in v0.1.2

func FlatMap[T any, R any](m Maybe[T], f func(T) Maybe[R]) Maybe[R]

func FromInterface added in v0.1.9

func FromInterface[T any](interfaceValue T) Maybe[T]

for interface type, not any

func FromPtr added in v0.1.8

func FromPtr[T any](ptr *T) Maybe[*T]

func FromPtrDereferenced added in v0.1.9

func FromPtrDereferenced[T any](ptr *T) Maybe[T]

func Map added in v0.1.2

func Map[T any, R any](m Maybe[T], f func(T) R) Maybe[R]

func NewMaybe

func NewMaybe[T any]() Maybe[T]

func None

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

func Some

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

func (Maybe[T]) Filter

func (m Maybe[T]) Filter(f func(T) bool) Maybe[T]

func (Maybe[T]) FlatMap

func (m Maybe[T]) FlatMap(f func(T) Maybe[T]) Maybe[T]

func (Maybe[T]) IsNone

func (m Maybe[T]) IsNone() bool

func (Maybe[T]) IsSome

func (m Maybe[T]) IsSome() bool

func (Maybe[T]) IsZero added in v0.1.5

func (m Maybe[T]) IsZero() bool

IsZero returns true if Maybe has no value. Used by encoding/json's 'omitzero' tag to omit missing fields.

func (Maybe[T]) Map

func (m Maybe[T]) Map(f func(T) T) Maybe[T]

func (Maybe[T]) OrElse

func (m Maybe[T]) OrElse(elseValue T) T

func (Maybe[T]) OrElseError

func (m Maybe[T]) OrElseError(err error) (T, error)

func (Maybe[T]) OrElseGet

func (m Maybe[T]) OrElseGet(f func() T) T

func (Maybe[T]) String added in v0.1.6

func (m Maybe[T]) String() string

func (Maybe[T]) ToPtr added in v0.1.8

func (m Maybe[T]) ToPtr() *T

func (Maybe[T]) Unwrap

func (m Maybe[T]) Unwrap() (T, error)

func (Maybe[T]) UnwrapOr added in v0.1.1

func (m Maybe[T]) UnwrapOr(defaultValue T) T

func (Maybe[T]) UnwrapUnsafe

func (m Maybe[T]) UnwrapUnsafe() T

Jump to

Keyboard shortcuts

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