opt

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 17, 2025 License: MIT Imports: 9 Imported by: 0

README

Opt

Go Doc

Opt is a Go package for safe abstractions over optional values.

Inspired by the Option type in Rust and follows the same ideas and function signatures.

Features

  • Represent explicitly set values. For example: {"b":2,"a":null} and {"b":2} would be different states for a - explicit and implicit None.
  • All the encoding and decoding functionality: json, gob, sql, text & binary.
  • Adapters to construct options from pointers, zero values, and proto messages.
  • No reflection.

Install

go get github.com/metafates/opt

Usage

See example_test.go for more examples.

package main

import (
	"fmt"
	"time"

	"github.com/metafates/opt"
)

type User struct {
	Birth opt.Opt[time.Time]
}

func (u User) Age() opt.Opt[int] {
	return opt.Map(u.Birth, func(t time.Time) int {
		return time.Now().Year() - t.Year()
	})
}

func isAdult(age int) bool {
	return age >= 18
}

func getUser(id int) opt.Opt[User] {
	// ...
}

func main() {
	if opt.AndThen(getUser(42), User.Age).IsSomeAnd(isAdult) {
		fmt.Println("🍺!")
	}
}

Documentation

Overview

Package opt provides safe abstractions over optional values.

Inspired by the Option type in Rust and follows the same ideas and function signatures.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Opt

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

Opt (option) represents an optional value. Every option is either Some and contains a value, or None, and does not.

Opt also separates explicitly set values, see Opt.IsExplicit. Implicit Some is unreachable state.

Use cases:

  • Initial values
  • Return values for functions that are not defined over their entire input range (partial functions)
  • Optional struct fields
  • Optional function arguments

func AndThen

func AndThen[T, U any](option Opt[T], f func(T) Opt[U]) Opt[U]

AndThen returns None if the option is None, otherwise calls `f` with the wrapped value and returns the result.

This function allows `f` to return a different type in contrast to the Opt.AndThen which is limited by the lack of method type parameters in Go.

Example
firstRune := func(s string) Opt[rune] {
	runes := []rune(s)

	if len(runes) == 0 {
		return None[rune]()
	}

	return Some(runes[0])
}

fmt.Println(AndThen(Some("яблоко"), firstRune))
fmt.Println(AndThen(Some(""), firstRune))
fmt.Println(AndThen(None[string](), firstRune))
Output:

Some(1103)
None
None

func FromProto

func FromProto[T proto.Message](msg T) Opt[T]

FromProto converts proto.Message to either Some value, if the message is valid, or None.

An invalid message is an empty, read-only value. An invalid message often corresponds to a nil pointer of the concrete message type, but the details are implementation dependent.

See [protoreflect.Message.IsValid]

func FromPtr

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

FromPtr returns Some with the underlying pointer value if it's not nil or None otherwise.

NOTE: Do not use this for converting [proto.Message]s, as they can not be dereferenced without breaking internal reflection mechanism. Use FromProto for that purpose

Example
value := 42

var x, y *int

x = &value
y = nil

fmt.Println(FromPtr(x))
fmt.Println(FromPtr(y))
Output:

Some(42)
None

func FromZero

func FromZero[T comparable](value T) Opt[T]

FromZero returns Some with the given value if it's not zero value or None otherwise.

This function requires value type to be comparable so that it can be checked for zero value without using reflection

Example
fmt.Println(FromZero(0))
fmt.Println(FromZero("foo"))
fmt.Println(FromZero(""))
Output:

None
Some(foo)
None

func IndexMap

func IndexMap[M ~map[K]V, K comparable, V any](m M, key K) Opt[V]

IndexMap returns Some map value at the given key if the key exists or None otherwise

Example
m := map[string]int{
	"a": 1,
}

fmt.Println(IndexMap(m, "a"))
fmt.Println(IndexMap(m, "b"))
Output:

Some(1)
None

func IndexSlice

func IndexSlice[S ~[]T, T any](slice S, index int) Opt[T]

IndexSlice returns Some slice value at the given index if the index exists or None otherwise

Example
s := []int{10, 40, 30}

fmt.Println(IndexSlice(s, 1))
fmt.Println(IndexSlice(s, 3))
Output:

Some(40)
None

func Map

func Map[T, U any](option Opt[T], f func(T) U) Opt[U]

Map maps a value by applying a function to a contained value (if Some) or returns None (if None).

This function allows `f` to return a different type in contrast to the Opt.Map which is limited by the lack of method type parameters in Go.

Example
maybeSomeString := Some("Hello, World!")
maybeSomeLen := Map(maybeSomeString, func(s string) int { return len(s) })

fmt.Println(maybeSomeLen)

x := None[string]()

fmt.Println(Map(x, func(s string) int { return len(s) }))
Output:

Some(13)
None

func None

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

None returns an option with no value. None option could also be defined like that:

var none Opt[any]
Example
x := None[int]()

fmt.Println(x)
Output:

None

func Some

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

Some returns option with some value

Example
x := Some(2)

fmt.Println(x)
Output:

Some(2)

func (Opt[T]) And

func (o Opt[T]) And(and Opt[T]) Opt[T]

And returns None if the option is None, otherwise returns `and`.

Example
x := Some(2)
y := None[int]()

fmt.Println(x.And(y))

x = None[int]()
y = Some(42)

fmt.Println(x.And(y))

x = Some(2)
y = Some(42)

fmt.Println(x.And(y))

x = None[int]()
y = None[int]()

fmt.Println(x.And(y))
Output:

None
None
Some(42)
None

func (Opt[T]) AndThen

func (o Opt[T]) AndThen(andThen func(T) Opt[T]) Opt[T]

AndThen returns None if the option is None, otherwise calls `andThen` with the wrapped value and returns the result.

See AndThen if you need to return a different type

Example
div42By := func(divider int) Opt[int] {
	if divider == 0 {
		return None[int]()
	}

	return Some(42 / divider)
}

fmt.Println(Some(2).AndThen(div42By))
fmt.Println(None[int]().AndThen(div42By))
fmt.Println(Some(0).AndThen(div42By))
Output:

Some(21)
None
None

func (Opt[T]) Filter

func (o Opt[T]) Filter(predicate func(T) bool) Opt[T]

Filter returns None if the option is None, otherwise calls predicate with the wrapped value and returns:

  • Some if predicate returns true.
  • None if predicate returns false.
Example
isEven := func(n int) bool { return n%2 == 0 }

fmt.Println(None[int]().Filter(isEven))
fmt.Println(Some(3).Filter(isEven))
fmt.Println(Some(4).Filter(isEven))
Output:

None
None
Some(4)

func (Opt[T]) GetOr

func (o Opt[T]) GetOr(or T) T

GetOr returns the contained Some value or a provided default.

Example
fmt.Println(Some("car").GetOr("bike"))
fmt.Println(None[string]().GetOr("bike"))
Output:

car
bike

func (Opt[T]) GetOrElse

func (o Opt[T]) GetOrElse(orElse func() T) T

GetOrElse returns the contained Some value or computes it from a function.

Example
k := 10

fmt.Println(Some(4).GetOrElse(func() int { return 2 * k }))
fmt.Println(None[int]().GetOrElse(func() int { return 2 * k }))
Output:

4
20

func (Opt[T]) GetOrEmpty

func (o Opt[T]) GetOrEmpty() T

GetOrEmpty returns the contained Some value or an empty value for this type.

Example
x := None[int]()
y := Some(12)

fmt.Println(x.GetOrEmpty())
fmt.Println(y.GetOrEmpty())
Output:

0
12

func (*Opt[T]) GobDecode

func (o *Opt[T]) GobDecode(data []byte) error

GobDecode implemenets gob.GobDecoder interface

func (Opt[T]) GobEncode

func (o Opt[T]) GobEncode() ([]byte, error)

GobEncode implemenets gob.GobEncoder interface

func (Opt[T]) Inspect

func (o Opt[T]) Inspect(f func(T)) Opt[T]

Inspect calls a function with a contained value if Some.

Returns the original option.

Example
x := Some("banana")
x.Inspect(func(s string) { fmt.Println(s) })

y := None[string]()
y.Inspect(func(s string) { fmt.Println(s) })
Output:

banana

func (Opt[T]) IsExplicit

func (o Opt[T]) IsExplicit() bool

IsExplicit reports whether this option was explicitly specified as either None or Some. This property is also applicable for decoded values, such as ones from json.Unmarshal.

If true, it is guaranteed that Opt.IsSome will also return true

This propery allows to represent the following states:

  • The value is not set
  • The value is explicitly set to either None or Some
Example
var implicit Opt[string]

explicit := None[string]()

fmt.Println(implicit, implicit.IsExplicit())
fmt.Println(explicit, explicit.IsExplicit())
Output:

None false
None true

func (Opt[T]) IsNone

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

IsNone returns true if the option is a None value.

Example
x := Some(2)
fmt.Println(x.IsNone())

x = None[int]()
fmt.Println(x.IsNone())
Output:

false
true

func (Opt[T]) IsNoneOr

func (o Opt[T]) IsNoneOr(orElse func(T) bool) bool

IsNoneOr returns true if the option is a None or the value inside of it matches a predicate.

Example
x := Some(2)
fmt.Println(x.IsNoneOr(func(x int) bool { return x > 1 }))

x = Some(0)
fmt.Println(x.IsNoneOr(func(x int) bool { return x > 1 }))

x = None[int]()
fmt.Println(x.IsNoneOr(func(x int) bool { return x > 1 }))
Output:

true
false
true

func (Opt[T]) IsSome

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

IsSome returns true if the option is a Some value.

If this option is Some it is guaranteed to be explicit. See Opt.IsExplicit

Example
x := Some(2)
fmt.Println(x.IsSome())

x = None[int]()
fmt.Println(x.IsSome())
Output:

true
false

func (Opt[T]) IsSomeAnd

func (o Opt[T]) IsSomeAnd(and func(T) bool) bool

IsSomeAnd returns true if the option is a Some and the value inside of it matches a predicate.

Example
x := Some(2)
fmt.Println(x.IsSomeAnd(func(x int) bool { return x > 1 }))

x = Some(0)
fmt.Println(x.IsSomeAnd(func(x int) bool { return x > 1 }))

x = None[int]()
fmt.Println(x.IsSomeAnd(func(x int) bool { return x > 1 }))
Output:

true
false
false

func (Opt[T]) Map

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

Map maps a value by applying a function to a contained value (if Some) or returns None (if None).

See Map if you need to return a different type.

Example
maybeSomeAge := Some(30)

maybeSomeYear := maybeSomeAge.Map(func(age int) int { return 2025 - age })

fmt.Println(maybeSomeYear)

x := None[int]()

fmt.Println(x.Map(func(n int) int { return n * 2 }))
Output:

Some(1995)
None

func (Opt[T]) MarshalBinary

func (o Opt[T]) MarshalBinary() ([]byte, error)

MarshalBinary implemenets encoding.BinaryMarshaler interface

func (Opt[T]) MarshalJSON

func (o Opt[T]) MarshalJSON() ([]byte, error)

MarshalJSON implemenets json.Marshaler interface

func (Opt[T]) MarshalText

func (o Opt[T]) MarshalText() ([]byte, error)

MarshalText implemenets encoding.TextMarshaler interface

func (Opt[T]) MustGet

func (o Opt[T]) MustGet() T

MustGet returns the contained Some value.

Panics if the self value equals None.

func (Opt[T]) Or

func (o Opt[T]) Or(or Opt[T]) Opt[T]

Or returns itself if it contains a value, otherwise returns `or`.

Example
x := Some(2)
y := None[int]()

fmt.Println(x.Or(y))

x = None[int]()
y = Some(100)

fmt.Println(x.Or(y))

x = Some(2)
y = Some(100)

fmt.Println(x.Or(y))

x = None[int]()
y = None[int]()

fmt.Println(x.Or(y))
Output:

Some(2)
Some(100)
Some(2)
None

func (Opt[T]) OrElse

func (o Opt[T]) OrElse(orElse func() Opt[T]) Opt[T]

OrElse returns itself if it contains a value, otherwise calls `orElse` and returns the result.

Example
nobody := func() Opt[string] { return None[string]() }
vikings := func() Opt[string] { return Some("vikings") }

fmt.Println(Some("barbarians").OrElse(vikings))
fmt.Println(None[string]().OrElse(vikings))
fmt.Println(None[string]().OrElse(nobody))
Output:

Some(barbarians)
Some(vikings)
None

func (*Opt[T]) Scan

func (o *Opt[T]) Scan(src any) error

Scan implements the sql.Scanner interface.

func (Opt[T]) String

func (o Opt[T]) String() string

func (Opt[T]) ToPtr

func (o Opt[T]) ToPtr() *T

ToPtr returns pointer to the value if the option is Some or nil otherwise.

The underlying value of the pointer is safe to modify, as it is copied before return to avoid changes to the original value.

Example
x := Some(42)
y := None[int]()

fmt.Println(x.ToPtr() != nil, *x.ToPtr())
fmt.Println(y.ToPtr())
Output:

true 42
<nil>

func (Opt[T]) TryGet

func (o Opt[T]) TryGet() (T, bool)

TryGet returns the contained Some value or an empty value for this type and boolean stating if this option is Some

If you only need a contained value or an empty one use Opt.GetOrEmpty

Example
x := Some(42)
y := None[int]()

fmt.Println(x.TryGet())
fmt.Println(y.TryGet())
Output:

42 true
0 false

func (*Opt[T]) UnmarshalBinary

func (o *Opt[T]) UnmarshalBinary(data []byte) error

UnmarshalBinary implemenets encoding.BinaryUnmarshaler interface

func (*Opt[T]) UnmarshalJSON

func (o *Opt[T]) UnmarshalJSON(b []byte) error

UnmarshalJSON implemenets json.Unmarshaler interface

func (*Opt[T]) UnmarshalText

func (o *Opt[T]) UnmarshalText(data []byte) error

UnmarshalText implemenets encoding.TextUnmarshaler interface

func (Opt[T]) Value

func (o Opt[T]) Value() (driver.Value, error)

Value implements the driver.Valuer interface.

Use unwrap methods (e.g. Opt.TryGet) instead for getting the go value

Jump to

Keyboard shortcuts

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