typact

package module
v0.5.1 Latest Latest
Warning

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

Go to latest
Published: Mar 4, 2025 License: MIT Imports: 15 Imported by: 1

README

typact

Go Report Card Godoc Reference Latest Release Coverage License

A zero dependency type action (typact) library for GoLang.


WARNING: This library has not reached v1 yet!

Installation

go get go.l0nax.org/typact

Examples

Examples can be found in the examples directory or at at the official Godoc page.

Usage

Implementing std.Cloner[T] for Option[T]

⚠ NOTE: This feature is marked as Unstable!

Because of some limitations of the GoLang type system, there are some important facts to remember when using the Clone method.

Custom Structs: Pointer Receiver

The best way to implement the Clone method is by using a pointer receiver like this:

type MyData struct {
  ID        int
  CreatedAt time.Time
  UpdatedAt time.Time
}

// Clone implements the std.Cloner interface.
func (m *MyData) Clone() *MyData {
  return &MyData{
    ID:        m.ID,
    CreatedAt: m.CreatedAt,
    UpdatedAt: m.UpdatedAt,
  }
}

This allows you to use your type with and without a pointer. For example:

var asPtr typact.Option[*MyData]
var asVal typact.Option[MyData]

Calling Clone on asPtr and asVal will result in a valid clone. This is because the Clone implementation of the Option[T] type checks if a type implements the std.Cloner interface with a pointer receiver.

For now it is not possible to implement the Clone method without a pointer receiver and use Clone on a pointer value:

type MyData struct {
  ID        int
  CreatedAt time.Time
  UpdatedAt time.Time
}

// Clone implements the std.Cloner interface.
func (m MyData) Clone() MyData {
  return MyData{
    ID:        m.ID,
    CreatedAt: m.CreatedAt,
    UpdatedAt: m.UpdatedAt,
  }
}

func main() {
  data := typact.Some(&MyData{
    ID:        15,
    CreatedAt: time.Now(),
    UpdatedAt: time.Now(),
  })
  data.Clone() // This will panic because *MyData does not implement std.Cloner.
}

Thus the best way to support all use-cases is to implement the interface with a pointer receiver.

Benchmarking has also shown that implementing std.Cloner[T] with a pointer receiver results in better performance for all use cases:

Benchmark Results
  goos: linux
goarch: amd64
pkg: go.l0nax.org/typact
cpu: AMD Ryzen 9 5900X 12-Core Processor
                                           │  /tmp/base   │
                                           │    sec/op    │
Option_Clone/None-24                         1.915n ±  1%
Option_Clone/String-24                       2.461n ±  1%
Option_Clone/Int64-24                        2.465n ±  1%
Option_Clone/CustomStructPointer-24          64.79n ±  3%
Option_Clone/CustomStructFallback-24         168.2n ±  9%
Option_Clone/ScalarSlice-24                  214.4n ±  4%

Option_Clone/PtrSliceWrapper_PtrRecv-24      1.537µ ±  4%
Option_Clone/SliceWrapper_PtrRecv-24         1.588µ ±  5%
Option_Clone/PtrSliceWrapper_NormalRecv-24   1.943µ ±  5%
Option_Clone/SliceWrapper_NormalRecv-24      1.887µ ± 15%
geomean                                      109.3n

                                           │  /tmp/base   │
                                           │     B/op     │
Option_Clone/None-24                         0.000 ± 0%
Option_Clone/String-24                       0.000 ± 0%
Option_Clone/Int64-24                        0.000 ± 0%
Option_Clone/CustomStructPointer-24          48.00 ± 0%
Option_Clone/CustomStructFallback-24         96.00 ± 0%
Option_Clone/ScalarSlice-24                  112.0 ± 0%

Option_Clone/PtrSliceWrapper_PtrRecv-24      328.0 ± 0%
Option_Clone/SliceWrapper_PtrRecv-24         408.0 ± 0%
Option_Clone/PtrSliceWrapper_NormalRecv-24   424.0 ± 0%
Option_Clone/SliceWrapper_NormalRecv-24      408.0 ± 0%
geomean                                                 ¹
¹ summaries must be >0 to compute geomean

                                           │  /tmp/base   │
                                           │  allocs/op   │
Option_Clone/None-24                         0.000 ± 0%
Option_Clone/String-24                       0.000 ± 0%
Option_Clone/Int64-24                        0.000 ± 0%
Option_Clone/CustomStructPointer-24          1.000 ± 0%
Option_Clone/CustomStructFallback-24         2.000 ± 0%
Option_Clone/ScalarSlice-24                  3.000 ± 0%

Option_Clone/PtrSliceWrapper_PtrRecv-24      11.00 ± 0%
Option_Clone/SliceWrapper_PtrRecv-24         11.00 ± 0%
Option_Clone/PtrSliceWrapper_NormalRecv-24   13.00 ± 0%
Option_Clone/SliceWrapper_NormalRecv-24      11.00 ± 0%
geomean                                                 ¹
¹ summaries must be >0 to compute geomean

Motivation

I've created this library because for one option types are really useful and prevent the one billion dollar mistake with dereferencing nil pointers (at least it reduces the risk).

At my work and within my private projects I often find myself in the position where

  • values may be provided (by the programmer, i.e. options)
  • values may be NULL (i.e. in a database/ JSON/ ...)

Whilst writing my own LSM implementation I frequently ran into the situation where specific checks are much easier to read when using a declarative approach (see the cmpop.Ordering type). Thus this library is not a Option type only library.

License

The project is licensed under the MIT License.

Documentation

Overview

Package typact ...

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Option

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

Option represents an optional value. Every Option is either Some and contains a value, or None, in which case it does not contain a value.

It is based on the std::option::Option type from Rust (https://doc.rust-lang.org/std/option/enum.Option.html).

func None

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

None returns Option with no value.

func Some

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

Some returns Option with val.

func TryWrap

func TryWrap[T any](fn func() (T, bool)) Option[T]

TryWrap executes fn and wraps the value in an Option.

func Wrap

func Wrap[T any](val T, some bool) Option[T]

Wrap wraps val and some into an Option.

To eagerly evaluate and wrap a value, use TryWrap.

func (Option[T]) And

func (o Option[T]) And(opt Option[T]) Option[T]

And returns None if o is None, otherwise opt is returned.

func (Option[T]) AndThen

func (o Option[T]) AndThen(fn func(T) Option[T]) Option[T]

AndThen returns None if o is none, otherwise fn is called and the return value is wrapped and returned.

func (Option[T]) Clone

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

Clone returns a deep copy of T, if o contains a value. Otherwise None is returned.

The below special types are handled by the method by simply copying the value:

  • Scalar types: all number-like types are copied by value.
  • string: Copied by value as string is immutable by design.
  • func: Copied by value as func is an opaque pointer at runtime.
  • unsafe.Pointer: Copied by value as we don't know what's in it.
  • chan: A new empty chan is created as we cannot read data inside the old chan.

WARN: If T is not part of the special types above AND not DOES NOT implement std.Cloner, this method will panic!

BUG: Currently unsupported are arrays of any type.

Unstable: This method is unstable and not guarded by the SemVer promise!

func (Option[T]) CloneWith added in v0.2.0

func (o Option[T]) CloneWith(fn func(T) T) Option[T]

CloneWith clones o by calling fn, if o contains a value.

Example (Custom)
package main

import (
	"fmt"
	"slices"

	"go.l0nax.org/typact"
)

func main() {
	cloner := func(x []string) []string {
		return slices.Clone(x)
	}

	x := typact.Some([]string{"Hello", "World"})
	y := x.CloneWith(cloner)

	// alter elem in x
	(*x.UnwrapAsRef())[0] = "Foo"

	fmt.Println(y.Unwrap())

}
Output:

[Hello World]

func (Option[T]) Deconstruct

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

Deconstruct returns the value and whether it is present.

Example
package main

import (
	"fmt"

	"go.l0nax.org/typact"
)

func main() {
	x := typact.Some("foo")

	val, ok := x.Deconstruct()
	fmt.Printf("%q, %t\n", val, ok)

	y := typact.None[string]()
	val, ok = y.Deconstruct()
	fmt.Printf("%q, %t\n", val, ok)

}
Output:

"foo", true
"", false

func (Option[T]) Expect

func (o Option[T]) Expect(msg string) T

Expect returns the contained value of o, if it is present. Otherwise it panics with msg.

func (Option[T]) Filter

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

Filter returns None if o is None, otherwise calls fn with the value of o and returns:

  • None if fn returns false
  • Some if fn returns true
Example (Slice)
package main

import (
	"fmt"
	"slices"

	"go.l0nax.org/typact"
)

func main() {
	x := []typact.Option[string]{
		typact.Some("foo"),
		typact.None[string](),
		typact.Some("bar"),
		typact.Some("baz"),
		typact.None[string](),
		typact.Some("hello"),
		typact.Some("world"),
	}

	x = slices.DeleteFunc(x, func(val typact.Option[string]) bool {
		return !val.IsSomeAnd(IsNotZero[string])
	})
	fmt.Println(x)

}

func IsNotZero[T comparable](val T) bool {
	var zero T
	return val != zero
}
Output:

[Some(foo) Some(bar) Some(baz) Some(hello) Some(world)]

func (*Option[T]) GetOrInsert

func (o *Option[T]) GetOrInsert(val T) *T

GetOrInsert inserts val in o if o is None. It subsequently returns a pointer to the value.

See [Insert], which updates the value even if the option already contains Some.

func (*Option[T]) GetOrInsertWith

func (o *Option[T]) GetOrInsertWith(fn func() T) *T

GetOrInsertWith inserts a value returned by fn into o, if o is None. It subsequently returns a pointer to the value.

Example
package main

import (
	"fmt"

	"go.l0nax.org/typact"
)

func main() {
	x := typact.None[int]()

	{
		y := x.GetOrInsertWith(func() int { return 2 })
		fmt.Println(*y)

		*y = 20
	}

	fmt.Println(x.Unwrap())

}
Output:

2
20

func (Option[T]) Hash added in v0.4.0

func (o Option[T]) Hash(h xhash.Hasher)

Hash implements the xhash.Hasher interface.

func (*Option[T]) Insert

func (o *Option[T]) Insert(val T) *T

Inserts val into o and returns the reference to the inserted value. If the option already contains a value, the old value is dropped.

See [GetOrInsert] which doesn't update the value if the option already contains Some.

Example
package main

import (
	"fmt"

	"go.l0nax.org/typact"
)

func main() {
	opt := typact.None[int]()
	val := opt.Insert(5)

	fmt.Println(*val)
	fmt.Println(opt.Unwrap())

	*val = 3
	fmt.Println(opt.Unwrap())

}
Output:

5
5
3

func (Option[T]) Inspect

func (o Option[T]) Inspect(fn func(T)) Option[T]

Inspect executes fn if o contains a value. It returns o.

Example
package main

import (
	"fmt"

	"go.l0nax.org/typact"
)

func main() {
	x := typact.Some("foo")
	x.Inspect(func(val string) {
		fmt.Println(val)
	})

	// Does print nothing
	y := typact.None[string]()
	y.Inspect(func(val string) {
		fmt.Println(val)
	})

}
Output:

foo

func (Option[T]) IsNone

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

IsNone returns true if o contains no value.

func (Option[T]) IsSome

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

IsSome returns true if o contains a value.

func (Option[T]) IsSomeAnd

func (o Option[T]) IsSomeAnd(fn func(T) bool) bool

IsSomeAnd returns true if o contains a value and fn(o) returns true.

Example
package main

import (
	"fmt"

	"go.l0nax.org/typact"
)

func main() {
	x := typact.Some("foo")

	ok := x.IsSomeAnd(func(str string) bool {
		return str == "foo"
	})
	fmt.Printf("%t\n", ok)

	ok = x.IsSomeAnd(func(str string) bool {
		return str == ""
	})
	fmt.Printf("%t\n", ok)

	y := typact.None[string]()
	ok = y.IsSomeAnd(func(str string) bool {
		return str != ""
	})
	fmt.Printf("%t\n", ok)

}
Output:

true
false
false

func (Option[T]) IsZero deprecated added in v0.2.0

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

IsZero returns whether o is None.

NOTE: This method has only been added for compatibility with unmarshaling/ marshaling libraries, e.g. YAML, TOML, JSON in conjunction with the "omitzero" tag.

Deprecated: Use Option.IsNone instead!

func (Option[T]) Map

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

Map maps Option[T] to Option[T] by calling fn on the value held by o, if Some. It returns Some with the new value returned by fn. Otherwise None will be returned.

func (Option[T]) MapOr

func (o Option[T]) MapOr(fn func(T) T, value T) T

MapOr returns the provided default result (if None), or applies fn to the contained value (if Some). Otherwise the provided (fallback) value is returned.

func (Option[T]) MapOrElse

func (o Option[T]) MapOrElse(mapFn func(T) T, valueFn func() T) T

MapOrElse applies the function mapFn to the value held by o if it exists, and returns the result. If o does not hold a value, it applies valueFn and returns its result.

This allows conditional transformation of the Option's value or generation of a default value when none is present.

func (Option[T]) MarshalJSON

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

UnmarshalJSON implements the json.Marshaler interface. If value is not present, 'null' be encoded.

func (Option[T]) MarshalTOML added in v0.4.0

func (o Option[T]) MarshalTOML() ([]byte, error)

MarshalTOML implements a TOML (v1) marshaler.

If T is not a scalar value and does not implement the TOMLMarshaler interface, it fallsback to Option.MarshalText and wraps it as string.

func (Option[T]) MarshalText

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

MarshalText implements the encoding.TextMarshaler interface.

Please not that for scalar types it is advised to define the "omitempty" tag!

func (Option[T]) Or

func (o Option[T]) Or(value Option[T]) Option[T]

Or returns the option if it contains a value, otherwise returns value.

func (Option[T]) OrElse

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

OrElse returns o, if Some. Otherwise the return value of valueFn is returned.

func (*Option[T]) Replace

func (o *Option[T]) Replace(val T) Option[T]

Replace replaces o with Some of val and returns the old value of o.

func (*Option[T]) Scan

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

Scan implements the sql.Scanner interface.

If *T implements sql.Scanner, the custom method will be called.

func (Option[T]) String added in v0.4.0

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

String implements the fmt.Stringer interface.

If it is Some, it calls the underlying value's fmt.Stringer method, if available.

func (*Option[T]) Take added in v0.2.0

func (o *Option[T]) Take() Option[T]

Take takes the value of o and returns it, leaving None in its place.

Experimental: This method is considered experimental and may change or be removed in the future.

func (*Option[T]) UnmarshalJSON

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

UnmarshalJSON implements the json.Unmarshaler interface.

func (*Option[T]) UnmarshalText

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

UnmarshalText implements the encoding.TextUnmarshaler interface.

NOTE: when using scalar types, it is advised use the "omitzero" tag!

func (Option[T]) UnsafeUnwrap

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

UnsafeUnwrap returns the value without checking whether the value is present.

WARN: Only use this method as a last resort!

func (Option[T]) Unwrap

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

Unwrap returns the value or panics if it is not present.

Example (None)
package main

import (
	"fmt"

	"go.l0nax.org/typact"
)

func main() {
	// WARN: This function panics!
	x := typact.None[string]()
	fmt.Println(x.Unwrap())
}
Output:

Example (Some)
package main

import (
	"fmt"

	"go.l0nax.org/typact"
)

func main() {
	x := typact.Some("foo")
	fmt.Println(x.Unwrap())

}
Output:

foo

func (*Option[T]) UnwrapAsRef

func (o *Option[T]) UnwrapAsRef() *T

UnwrapAsRef unwraps o and returns the reference to the value. If the value is not present, this method will panic.

func (Option[T]) UnwrapOr

func (o Option[T]) UnwrapOr(value T) T

UnwrapOr returns the value of o, if present. Otherwise the provided value is returned

Example
package main

import (
	"fmt"

	"go.l0nax.org/typact"
)

func main() {
	fmt.Println(typact.Some("foo").UnwrapOr("bar"))
	fmt.Println(typact.None[string]().UnwrapOr("world"))

}
Output:

foo
world

func (Option[T]) UnwrapOrElse

func (o Option[T]) UnwrapOrElse(fn func() T) T

UnwrapOrElse returns the value of o, if present. Otherwise it executes fn and returns the value.

func (Option[T]) UnwrapOrZero

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

UnwrapOrZero returns the value of o, if present Otherwise the zero value of T is returned

func (Option[T]) Value

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

Value implements the driver.Valuer interface. It returns NULL if o is None, otherwise it returns the value of o.

If T implements the driver.Valuer interface, the method will be called instead.

Directories

Path Synopsis
examples
internal
features
Package features helps to determine which GoLang/ CPU/ ...
Package features helps to determine which GoLang/ CPU/ ...
types
Package types provices type helper to ease the development when working with types.
Package types provices type helper to ease the development when working with types.
std
Package std provides standard type definitions and helper for typact.
Package std provides standard type definitions and helper for typact.
exp
Package exp holds experimental packages.
Package exp holds experimental packages.
exp/cmpop
Package cmpop provides types and functions to easy compare operations.
Package cmpop provides types and functions to easy compare operations.
exp/immutable
Package immutable provides immutable collection types.
Package immutable provides immutable collection types.
exp/iterops
Package iterops provides types and functions to ease working with iterators and iterating in general.
Package iterops provides types and functions to ease working with iterators and iterating in general.
exp/pred
Package pred provides helper functions which are often used to validate/ filter input.
Package pred provides helper functions which are often used to validate/ filter input.
exp/xslices
Package xslices is the addition/ extension to the official slices package.
Package xslices is the addition/ extension to the official slices package.
exp/xtypact
Package xtypact is the EXPERIMENTAL package of typact.
Package xtypact is the EXPERIMENTAL package of typact.
option
Package option provides helper functions to work with the typact.Option type.
Package option provides helper functions to work with the typact.Option type.
randx
Package randx provides random helper.
Package randx provides random helper.
xhash
Package xhash provides generic hashing support for all types in Go.
Package xhash provides generic hashing support for all types in Go.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳