Skip to content

Commit

Permalink
bools and pointers
Browse files Browse the repository at this point in the history
  • Loading branch information
jhbforlife committed Jan 9, 2024
1 parent 06b8cf7 commit e66db9d
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 198 deletions.
110 changes: 54 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,91 +6,72 @@

## Package Examples

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

// Easily set defaults
n := getUserInput()
defaults.SetFirst(&n, 42)
```

### `truthy/is`
```go
// is.Truthy returns whether a comparable value
// bools.Comparable returns whether a comparable value
// is equal to the zero value for its type.
//
// is.TruthyAny returns the truthiness of any argument.
// bools.Any 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.

is.Truthy(0) // false
is.Truthy(1) // true
bools.Comparable(0) // false
bools.Comparable(1) // true

is.Truthy("") // false
is.Truthy(" ") // true
bools.Comparable("") // false
bools.Comparable(" ") // true

is.TruthySlice([]byte(``)) // false
is.TruthySlice([]byte(` `)) // true
bools.Map(map[string]string{}) // false
bools.Map(map[string]string{"truthy": "is awesome"}) // true

is.TruthySlice([]int{}) // false
is.TruthySlice([]int{1, 2, 3}) // true
bools.Slice([]int{}) // false
bools.Slice([]int{1, 2, 3}) // true

var err error
is.Truthy(err) // false
is.Truthy(errors.New("hi")) // true
if is.Truthy(err) {
bools.Comparable(err) // false
bools.Comparable(errors.New("hi")) // true
if bools.Comparable(err) {
panic(err)
}


var p *int
is.Truthy(p) // false
bools.Comparable(p) // false

p = new(int)
// Truthy does not check the underlying truthy value for a pointer!
is.Truthy(p) // true
// for that, use TruthyPointer,
is.TruthyPointer(p) // false
// Comparable does not check the underlying truthy value for a pointer!
bools.Comparable(p) // true
// for that, use Pointer,
bools.Pointer(p) // false
// but beware of double pointers!
is.TruthyPointer(&p) // true
```
bools.Pointer(&p) // true

### `truthy/pointers`
```go
// Start with a null pointer
var strptr *string

// pointers.Deref safely dereferences a nil pointer into its empty value
fmt.Println(pointers.Deref(strptr) == "") // prints true

// pointers.Coalesce lets us specify a default value for a nil pointer
fmt.Println(pointers.Coalesce(strptr, "hello, world")) // prints "hello, world"
// How about an equivalent of the nullish coalescing operator ??
// as seen in C#, JavaScript, PHP, etc.:
var s string
bools.GetFirst(s, "default") // "default"
s = "something"
bools.GetFirst(s, "default") // "something"
bools.GetFirst(0, 0*1, 1-1, 0x10-10) // 6

// We can create a pointer to a string or other primitive type with pointers.New
newptr := pointers.New("meaning of life") // makes a pointer to a string, wow!
// Easily set defaults
n := getUserInput()
bools.SetFirst(&n, 42)

// pointers.First returns the first pointer that isn't nil.
strptr = pointers.First(strptr, newptr) // returns newptr
```
// Want to be able to convert from Boolean to Int?
bools.Int(true) // 1
bools.Int(10 > 100) // 0
bools.Int(bools.Comparable("")) // 0

### `truthy/ternary`
```go
// Ever wish Go had ? : ternary operators?
// Now it has a ternary function.
x := ternary.Evaluate(is.Truthy(""), 1, 10) // x == 10
x := bools.Ternary(bools.Comparable(""), 1, 10) // x == 10

// ternary.Evaluate cannot lazily evaluate its arguments,
// bools.Ternary cannot lazily evaluate its arguments,
// but you can use a closure to fake it.
s := ternary.Evaluate(is.TruthySlice([]string{""}),
s := bools.Ternary(bools.Slice([]string{""}),
func() string {
// do some calculation
return "foo"
Expand All @@ -101,3 +82,20 @@ s := ternary.Evaluate(is.TruthySlice([]string{""}),
})()
// s == "foo"
```

### `truthy/pointers`
```go
// Start with a null pointer
var strptr *string

// pointers.Deref safely dereferences a nil pointer into its empty value
fmt.Println(pointers.Deref(strptr) == "") // prints true

// pointers.Coalesce lets us specify a default value for a nil pointer
fmt.Println(pointers.Coalesce(strptr, "hello, world")) // prints "hello, world"

// We can create a pointer to a string or other primitive type with pointers.New
newptr := pointers.New("meaning of life") // makes a pointer to a string, wow!

// pointers.First returns the first pointer that isn't nil.
strptr = pointers.First(strptr, newptr) // returns newptr
29 changes: 15 additions & 14 deletions is/is.go → bools/bools.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package is
package bools

import (
"reflect"
)

// Truthy returns the truthy value of comparable types.
// Comparable returns the truthy value of comparable types.
// Values are truthy if they are not equal to the zero value for the type.
// Note that it does not evaluate the zero value for pointer types.
func Truthy[T comparable](v T) bool {
// Use the Pointer method to evaluate underlying pointer values, otherwise
// this will return true if the pointer is not nil.
func Comparable[T comparable](v T) bool {
return v != *new(T)
}

// TruthyAny returns the truthy value of anything.
// Any 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.
Expand All @@ -20,9 +21,9 @@ func Truthy[T comparable](v T) bool {
// Note that the usual Go type system caveats apply around a nil pointer value not being a nil interface value.
//
// In benchmark testing,
// TruthyAny is approximately 25 times slower than Truthy,
// Any is approximately 25 times slower than Comparable,
// and 50 times slower than native Go comparisons.
func TruthyAny[T any](v T) bool {
func Any[T any](v T) bool {
switch m := any(v).(type) {
case interface{ Bool() bool }:
return m.Bool()
Expand All @@ -32,25 +33,25 @@ func TruthyAny[T any](v T) bool {
return reflectValue(&v)
}

// TruthyMap returns true if the length of the map is greater than 0.
// Map returns true if the length of the map is greater than 0.
// Note that it does not distinguish nil maps from empty maps.
func TruthyMap[K comparable, V any, M ~map[K]V](v M) bool {
func Map[K comparable, V any, M ~map[K]V](v M) bool {
return len(v) > 0
}

// TruthyPointer returns the truthy value of dereferenced pointers of comparable types.
// Pointer returns the truthy value of dereferenced pointers for comparable types.
// Values are truthy if they are not equal to the zero value for the dereferenced type.
// Note that it will evaluate to true for double pointers.
func TruthyPointer[T comparable](v *T) bool {
func Pointer[T comparable](v *T) bool {
if v == nil {
return false
}
return Truthy(*v)
return Comparable(*v)
}

// TruthySlice returns true if the length of the slice is greater than 0.
// Slice returns true if the length of the slice is greater than 0.
// Note that it does not distinguish nil slices from empty slices.
func TruthySlice[T any, S ~[]T](v S) bool {
func Slice[T any, S ~[]T](v S) bool {
return len(v) > 0
}

Expand Down
8 changes: 4 additions & 4 deletions is/is_for_any_test.go → bools/bools_any_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package is_test
package bools_test

import (
"fmt"
"testing"
"time"

"github.com/carlmjohnson/truthy/is"
"github.com/carlmjohnson/truthy/bools"
)

type errT struct{}
Expand All @@ -14,13 +14,13 @@ func (*errT) Error() string { return "" }

func test[T any](t *testing.T, v T, ok bool) {
t.Run(fmt.Sprintf("%T-%v-%v", v, v, ok), func(t *testing.T) {
if got := is.TruthyAny(v); got != ok {
if got := bools.Any(v); got != ok {
t.Fatal()
}
})
}

func TestTruthyAny(t *testing.T) {
func TestAny(t *testing.T) {
var err error
test(t, err, false)
err = (*errT)(nil)
Expand Down
28 changes: 14 additions & 14 deletions is/is_bench_test.go → bools/bools_bench_test.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
package is_test
package bools_test

import (
"errors"
"testing"

"github.com/carlmjohnson/truthy/is"
"github.com/carlmjohnson/truthy/bools"
)

func BenchmarkTruthyAny_error(b *testing.B) {
func BenchmarkAny_error(b *testing.B) {
fillVal := errors.New("something")
fill := false
for i := 0; i < b.N; i++ {
var value error
if fill {
value = fillVal
}
if is.TruthyAny(value) != fill {
if bools.Any(value) != fill {
b.FailNow()
}
fill = !fill
}
}

func BenchmarkTruthy_error(b *testing.B) {
func BenchmarkComparable_error(b *testing.B) {
fillVal := errors.New("something")
fill := false
for i := 0; i < b.N; i++ {
var value error
if fill {
value = fillVal
}
if is.Truthy(value) != fill {
if bools.Comparable(value) != fill {
b.FailNow()
}
fill = !fill
Expand All @@ -52,30 +52,30 @@ func Benchmark_error(b *testing.B) {
}
}

func BenchmarkTruthyAny_string(b *testing.B) {
func BenchmarkAny_string(b *testing.B) {
fillVal := "something"
fill := false
for i := 0; i < b.N; i++ {
var value string
if fill {
value = fillVal
}
if is.TruthyAny(value) != fill {
if bools.Any(value) != fill {
b.FailNow()
}
fill = !fill
}
}

func BenchmarkTruthy_string(b *testing.B) {
func BenchmarkComparable_string(b *testing.B) {
fillVal := "something"
fill := false
for i := 0; i < b.N; i++ {
var value string
if fill {
value = fillVal
}
if is.Truthy(value) != fill {
if bools.Comparable(value) != fill {
b.FailNow()
}
fill = !fill
Expand All @@ -97,30 +97,30 @@ func Benchmark_string(b *testing.B) {
}
}

func BenchmarkTruthyAny_int(b *testing.B) {
func BenchmarkAny_int(b *testing.B) {
fillVal := 1
fill := false
for i := 0; i < b.N; i++ {
var value int
if fill {
value = fillVal
}
if is.TruthyAny(value) != fill {
if bools.Any(value) != fill {
b.FailNow()
}
fill = !fill
}
}

func BenchmarkTruthy_int(b *testing.B) {
func BenchmarkComparable_int(b *testing.B) {
fillVal := 1
fill := false
for i := 0; i < b.N; i++ {
var value int
if fill {
value = fillVal
}
if is.Truthy(value) != fill {
if bools.Comparable(value) != fill {
b.FailNow()
}
fill = !fill
Expand Down
Loading

0 comments on commit e66db9d

Please sign in to comment.