truthy

package module
v0.23.1 Latest Latest
Warning

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

Go to latest
Published: Feb 20, 2023 License: MIT Imports: 1 Imported by: 3

README

Truthy Go Reference

Truthiness

Truthy is a package which uses generics to create useful boolean tests of truthiness and helper functions.

Examples

// truthy.Value returns whether a comparable value
// is equal to the zero value for its type.
//
// truthy.ValueAny returns the truthiness of any argument.
// If the value's type has a Bool() bool method, the method is called and returned.
// If the type has an IsZero() bool method, the opposite value is returned.
// Slices and maps are truthy if they have a length greater than zero.
// All other types are truthy if they are not their zero value.

truthy.Value(0) // false
truthy.Value(1) // true

truthy.Value("") // false
truthy.Value(" ") // true

truthy.ValueSlice([]byte(``)) // false
truthy.ValueSlice([]byte(` `)) // true

truthy.ValueSlice([]int{}) // false
truthy.ValueSlice([]int{1, 2, 3}) // true

var err error
truthy.Value(err) // false
truthy.Value(errors.New("hi")) // true
if truthy.Value(err) {
	panic(err)
}


var p *int
truthy.Value(p) // false

p = new(int)
// truthy does not check value underlying pointer!
truthy.Value(p) // true

// Ever wish Go had ? : ternary operators?
// Now it has a ternary function.
x := truthy.Cond(truthy.Value(""), 1, 10) // x == 10

// truthy.Cond cannot lazily evaluate its arguments,
// but you can use a closure to fake it.
s := truthy.Cond(truthy.ValueSlice([]string{""}),
	func() string {
		// do some calculation
		return "foo"
	},
	func() string {
		// do some calculation
		return "bar"
	})()
// s == "foo"


// How about an equivalent of the nullish coalescing operator ??
// as seen in C#, JavaScript, PHP, etc.:
var s string
truthy.First(s, "default") // "default"
s = "something"
truthy.First(s, "default") // "something"
truthy.First(0, 0*1, 1-1, 0x10-10) // 6

// Easily set defaults
n := getUserInput()
truthy.SetDefault(&n, 42)

FAQs

Oh god

This is the correct reaction.

Isn't this just using reflection? Does it even really require generics?

I tried to write a non-generic version of this package first, but you can’t reflect on an interface type. When you do reflect.Value(x), you lose the fact that x was, e.g. an error, because reflect.Value() only takes interface{} and the conversion loses the interface type. You’d end up saying whether the underlying concrete type was empty or not, which is typically not what you want. To work around that, you could require that everything is passed as a pointer, e.g. reflect.Value(&err), but truthy.Value(&err) sucks as an API. If you look at how truthy.Value() works, it accepts a value of type T, and then passes *T to reflect.Value() and calls value.Elem() to finally get the correct reflection type. So, on a technical level, you couldn’t quite make this API work without generics, although it could be close. However, truthy.Filter(), truthy.SetDefault(), truthy.Any(), and truthy.All() could be implemented with pure reflection, although the implementation would be a lot uglier.

Then there’s truthy.First(). To be honest, truthy.First() is the only part of the package that I consider actually useful, and even that, I mostly expect it to be used for picking a string or default. Anyhow, it requires generics to avoid the cast back from interface type to the concrete type.

Should I use this package?

Probably not. It's a little bit of a joke package, but the truthy.First() and truthy.SetDefault() functionality seem useful, especially for strings. Time will tell what best practices around the use of generics in Go turn out to be.

Documentation

Overview

Package truthy has boolean tests of truthiness and helper functions.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Coalesce added in v0.23.1

func Coalesce[T any](p *T, v T) T

Coalesce returns *p if p is not nil, otherwise v.

Example
package main

import (
	"fmt"

	"github.com/carlmjohnson/truthy"
)

func main() {
	var np *int
	fmt.Println(truthy.Coalesce(np, 1))
	np = new(int)
	fmt.Println(truthy.Coalesce(np, 1))
}
Output:

1
0

func Cond

func Cond[T any](cond bool, ifVal, elseVal T) T

Cond returns ifVal if cond is true, otherwise it returns elseVal.

Example (Lazy)
package main

import (
	"fmt"

	"github.com/carlmjohnson/truthy"
)

func main() {
	i := 1
	// Cond cannot lazily evaluate its arguments,
	// but you can use a closure to fake it.
	s := truthy.Cond(
		truthy.Value(i),
		func() string {
			// do some calculation
			return "true"
		},
		func() string {
			// do some calculation
			return "false"
		})()
	fmt.Printf("%q\n", s)
}
Output:

"true"

func Deref added in v0.23.1

func Deref[T any](p *T) T

Deref returns *p if p is not nil, otherwise the zero value of T.

Example
package main

import (
	"fmt"

	"github.com/carlmjohnson/truthy"
)

func main() {
	var np *int
	fmt.Println(truthy.Deref(np))
	one := 1
	np = &one
	fmt.Println(truthy.Deref(np))
}
Output:

0
1

func First

func First[T comparable](vs ...T) (t T)

First returns the first value in vs which is non-zero.

Example
package main

import (
	"fmt"
	"time"

	"github.com/carlmjohnson/truthy"
)

type MyStruct struct {
	Port    int
	Host    string
	Timeout time.Duration
}

func MakeMyStruct(port int, host string, timeout time.Duration) *MyStruct {
	return &MyStruct{
		Port:    truthy.First(port, 80),
		Host:    truthy.First(host, "localhost"),
		Timeout: truthy.First(timeout, 10*time.Second),
	}
}

func main() {
	fmt.Println(MakeMyStruct(0, "", 0))
	fmt.Println(MakeMyStruct(443, "example.com", 1*time.Minute))
}
Output:

&{80 localhost 10s}
&{443 example.com 1m0s}

func FirstAny added in v0.23.1

func FirstAny[T any](vs ...T) (t T)

FirstAny returns the first value in vs which is truthy.

func SetDefault

func SetDefault[T comparable](p *T, defaults ...T)

SetDefault sets p to the first non-zero value in defaults if it is not already non-zero.

Example
package main

import (
	"fmt"
	"time"

	"github.com/carlmjohnson/truthy"
)

type MyStruct struct {
	Port    int
	Host    string
	Timeout time.Duration
}

func NewMyStruct(port int, host string, timeout time.Duration) *MyStruct {
	s := MyStruct{port, host, timeout}
	truthy.SetDefault(&s.Port, 80)
	truthy.SetDefault(&s.Host, "localhost")
	truthy.SetDefault(&s.Timeout, 10*time.Second)
	return &s
}

func main() {
	fmt.Println(NewMyStruct(0, "", 0))
	fmt.Println(NewMyStruct(443, "example.com", 1*time.Minute))
}
Output:

&{80 localhost 10s}
&{443 example.com 1m0s}

func SetDefaultAny added in v0.23.1

func SetDefaultAny[T any](p *T, defaults ...T)

SetDefaultAny sets p to the first truthy value in defaults if it is not already truthy.

func Value

func Value[T comparable](v T) bool

Value returns the truthy value of comparable types. Values are truthy if they are not equal to the zero value for the type.

Example
package main

import (
	"errors"
	"fmt"
	"time"

	"github.com/carlmjohnson/truthy"
)

func main() {
	var err error
	fmt.Println(truthy.Value(err))

	err = errors.New("hi")
	fmt.Println(truthy.Value(err))

	var n int
	fmt.Println(truthy.Value(n))

	n = 1
	fmt.Println(truthy.Value(n))

	var p *int
	fmt.Println(truthy.Value(p))

	p = new(int)
	// truthy does not check value underlying pointer!
	fmt.Println(truthy.Value(p))

	var s string
	fmt.Println(truthy.Value(s))

	s = " "
	fmt.Println(truthy.Value(s))

	var b []byte
	fmt.Println(truthy.ValueSlice(b))

	b = []byte(" ")
	fmt.Println(truthy.ValueSlice(b))

	m := map[string]string{}
	fmt.Println(truthy.ValueMap(m))

	m["a"] = "b"
	fmt.Println(truthy.ValueMap(m))

	var t time.Time
	t = t.Local()
	// t.IsZero() is still true although t is not the empty value
	fmt.Println(truthy.ValueAny(t))

	t = t.Add(1 * time.Second)
	fmt.Println(truthy.ValueAny(t))

}
Output:

false
true
false
true
false
true
false
true
false
true
false
true
false
true

func ValueAny added in v0.23.1

func ValueAny[T any](v T) bool

ValueAny returns the truthy value of anything. If the value's type has a Bool() bool method, the method is called and returned. If the type has an IsZero() bool method, the opposite value is returned. Slices and maps are truthy if they have a length greater than zero. All other types are truthy if they are not their zero value.

Note that the usual Go type system caveats apply around a nil pointer value not being a nil interface value.

In benchmark testing, ValueAny is approximately 25 times slower than Value, and 50 times slower than native Go comparisons.

func ValueMap added in v0.23.1

func ValueMap[K comparable, V any, M ~map[K]V](v M) bool

ValueMap returns true if the length of the map is greater than 0. Note that it does not distinguish nil maps from empty maps.

func ValueSlice added in v0.23.1

func ValueSlice[T any, S ~[]T](v S) bool

ValueSlice returns true if the length of the slice is greater than 0. Note that it does not distinguish nil slices from empty slices.

Types

This section is empty.

Jump to

Keyboard shortcuts

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