Skip to content

Commit 4eaa1d6

Browse files
committed
Fixes
1 parent 1f48f70 commit 4eaa1d6

File tree

10 files changed

+114
-75
lines changed

10 files changed

+114
-75
lines changed
File renamed without changes.

fun/partial/partial.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
// bound.
99
//
1010
// Functions have the prefix "Left" if they bind the left-most argument first,
11-
// or "Right" if they bind the right-most argument first.
11+
// or "Right" if they bind the right-most argument first. The prefix "All"
12+
// binds all arguments at once.
1213
//
1314
// Each input function must return exactly one value. The "fun/result" and
1415
// "fun/maybe" packages provide useful functions that can wrap functions that

fun/promise/promise.go

+27-18
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
// represent a computation to be performed at a later stage.
33
//
44
// This composes nicely with the idea of futures (see fun/future).
5+
//
6+
// Tip: rarely return a Promise from any function. Instead, return a normal
7+
// (result, error) tuple. The caller can use WrapResultFunc to transform that
8+
// function into one that returns a promise.
59
package promise
610

711
import (
@@ -30,13 +34,13 @@ type P[X any] interface {
3034
ComputeCtx(ctx context.Context) (X, error)
3135
}
3236

33-
// Func is the type of a function with no arguments that satisfies the promise
37+
// pfunc is the type of a function with no arguments that satisfies the promise
3438
// interface by calling the function, ignoring any context.
35-
type Func[X any] func() (X, error)
36-
func (f Func[X]) Compute() (X, error) {
39+
type pfunc[X any] func() (X, error)
40+
func (f pfunc[X]) Compute() (X, error) {
3741
return f()
3842
}
39-
func (f Func[X]) ComputeCtx(ctx context.Context) (X, error) {
43+
func (f pfunc[X]) ComputeCtx(context.Context) (X, error) {
4044
return f()
4145
}
4246

@@ -51,7 +55,7 @@ func FromFunc[T any](f func() T) P[T] {
5155
// FromResultFunc creates a promise to call function f, where f returns a
5256
// (result, error) tuple.
5357
func FromResultFunc[X any](f func() (X, error)) P[X] {
54-
return Func[X](f)
58+
return pfunc[X](f)
5559
}
5660

5761
// WrapResultFunc wraps an existing function "f() => (X, error)" so that it
@@ -98,20 +102,20 @@ func WrapResultFunc4[A, B, C, D, X any](f func(A, B, C, D) (X, error)) func(A, B
98102
// (value, ok) tuple. If the returned ok is false, the promise computes the
99103
// error [NotOk].
100104
func FromOkFunc[X any](f func() (X, bool)) P[X] {
101-
return Func[X](func() (result X, err error) {
105+
return pfunc[X](func() (result X, err error) {
102106
v, ok := f()
103107
if !ok { err = NotOk; return }
104108
return v, nil
105109
})
106110
}
107111

108-
// FuncCtx is the type of a function with a context argument that satisfies the
112+
// pfuncCtx is the type of a function with a context argument that satisfies the
109113
// promise interface by calling the function with a context.
110-
type FuncCtx[X any] func(ctx context.Context) (X, error)
111-
func (f FuncCtx[X]) Compute() (X, error) {
114+
type pfuncCtx[X any] func(ctx context.Context) (X, error)
115+
func (f pfuncCtx[X]) Compute() (X, error) {
112116
return f(context.TODO())
113117
}
114-
func (f FuncCtx[X]) ComputeCtx(ctx context.Context) (X, error) {
118+
func (f pfuncCtx[X]) ComputeCtx(ctx context.Context) (X, error) {
115119
return f(ctx)
116120
}
117121

@@ -127,32 +131,34 @@ func FromFuncCtx[T any](f func(context.Context) T) P[T] {
127131
// FromResultFuncCtx creates a promise to call function f, where f accepts a
128132
// context and returns a (result, error) tuple.
129133
func FromResultFuncCtx[X any](f func(context.Context) (X, error)) P[X] {
130-
return FuncCtx[X](f)
134+
return pfuncCtx[X](f)
131135
}
132136

133137
// FromOkFuncCtx creates a promise to call function f, where f accepts a
134138
// context and returns a (value, ok) tuple. If the returned ok is false, the
135139
// promise computes the error [NotOk].
136140
func FromOkFuncCtx[X any](f func(ctx context.Context) (X, bool)) P[X] {
137-
return FuncCtx[X](func(ctx context.Context) (result X, err error) {
141+
return pfuncCtx[X](func(ctx context.Context) (result X, err error) {
138142
v, ok := f(ctx)
139143
if !ok { err = NotOk; return }
140144
return v, nil
141145
})
142146
}
143147

144-
type value[X any] struct {x X}
145-
func (v value[X]) Compute() (X, error) {
148+
// pvalue is the type of a single value that implements the promise interface
149+
// by always returning the value.
150+
type pvalue[X any] struct {x X}
151+
func (v pvalue[X]) Compute() (X, error) {
146152
return v.x, nil
147153
}
148-
func (v value[X]) ComputeCtx(ctx context.Context) (X, error) {
154+
func (v pvalue[X]) ComputeCtx(context.Context) (X, error) {
149155
return v.x, nil
150156
}
151157

152158
// FromValue creates a promise that simply returns the provided argument and
153159
// a nil error when computed.
154160
func FromValue[X any](x X) P[X] {
155-
return value[X]{x: x}
161+
return pvalue[X]{x: x}
156162
}
157163

158164
// FromValueErr creates a promise that simply returns the provided argument or,
@@ -165,12 +171,14 @@ func FromValueErr[X any](value X, err error) P[X] {
165171
}
166172
}
167173

174+
// perror is the type of a single error that implements the promise interface
175+
// by always returning the error.
168176
type perror[X any] struct {err error}
169177
func (e perror[X]) Compute() (X, error) {
170178
var zero X
171179
return zero, e.err
172180
}
173-
func (e perror[X]) ComputeCtx(ctx context.Context) (X, error) {
181+
func (e perror[X]) ComputeCtx(context.Context) (X, error) {
174182
var zero X
175183
return zero, e.err
176184
}
@@ -182,7 +190,8 @@ func FromError[X any](err error) P[X] {
182190
}
183191

184192
// Chain returns a new promise to compute function f on the result of promise
185-
// p.
193+
// p. If the input promise returns an error, the returned promise will return
194+
// that error without calling f.
186195
func Chain[X any, Y any](p P[X], f func(X) (Y, error)) P[Y] {
187196
return FromResultFuncCtx[Y](func(ctx context.Context) (Y, error) {
188197
v, err := p.ComputeCtx(ctx)

fun/slices/slices.go

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
// Package slices provides generic higher-order functions over slices of values.
22
package slices
33

4+
// Copy deeply copies a slice of elements that each, in turn, must be copied
5+
// using their own Copy method.
6+
func Copy[X interface{Copy() X}](xs []X) []X {
7+
results := []X(nil)
8+
for _, x := range xs {
9+
results = append(results, x.Copy())
10+
}
11+
return results
12+
}
13+
414
// FromArgs returns the slice of the variadic arguments list.
515
func FromArgs[X any](xs ... X) []X {
616
return xs

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,23 @@ github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPp
55
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
66
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
77
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8+
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
89
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
910
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1011
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
12+
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
1113
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
1214
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
1315
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
1416
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
1517
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
1618
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
19+
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
1720
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
1821
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1922
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
2023
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
24+
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
2125
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2226
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2327
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

operator/checked/examples_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"github.com/tawesoft/golib/v2/operator/checked"
77
)
88

9+
// ExampleSimple demonstrates checked addition against the built-in limits
10+
// for 8 bit unsigned integers.
911
func ExampleSimple() {
1012
{
1113
result, ok := checked.Uint8.Add(250, 5)
@@ -22,14 +24,17 @@ func ExampleSimple() {
2224
// checked.Uint8.Add(250, 6): 0, ok?=false
2325
}
2426

27+
// ExampleLimits demonstrates checked subtraction against custom limits.
2528
func ExampleLimits() {
29+
// Call checked.Sub directly with min and max...
2630
{
2731
const min = 0
2832
const max = 99
2933
result, ok := checked.Sub(min, max, 10, 9)
3034
fmt.Printf("checked.Sub(min, max, 10, 9): %d, ok?=%t\n", result, ok)
3135
}
3236

37+
// Or define a custom limit and call the Sub() method...
3338
{
3439
limit := checked.Limits[int]{Min: 0, Max: 99}
3540
result, ok := limit.Sub(10, 25)

operator/checked/integer.go

+30-31
Original file line numberDiff line numberDiff line change
@@ -6,58 +6,57 @@ import (
66
"golang.org/x/exp/constraints"
77
)
88

9-
// Add returns (a + b, true) iff the result lies between min and max inclusive,
10-
// otherwise returns (0, false). This calculation is robust in the event of
11-
// integer overflow.
12-
//
13-
// The input arguments must satisfy the inequalities: `min <= a <= max` and
14-
// `min <= b <= max`.
9+
// Add returns (a + b, true) iff a, b, and the result all lie between min and
10+
// max inclusive, otherwise returns (0, false). This calculation is robust in
11+
// the event of integer overflow.
1512
func Add[N constraints.Integer](min N, max N, a N, b N) (N, bool) {
13+
if (a < min) || (a > max) || (b < min) || (b > max) { return 0, false }
14+
if (min > max) || (max < min) { return 0, false }
1615
if (b > 0) && (a > (max - b)) { return 0, false }
1716
if (b < 0) && (a < (min - b)) { return 0, false }
1817
return a + b, true
1918
}
2019

21-
// Sub returns (a + b, true) iff the result lies between min and max inclusive,
22-
// otherwise returns (0, false). This calculation is robust in the event of
23-
// integer overflow.
24-
//
25-
// The input arguments must satisfy the inequalities: `min <= a <= max` and
26-
// `min <= b <= max`.
20+
// Sub returns (a - b, true) iff a, b, and the result all lie between min and
21+
// max inclusive, otherwise returns (0, false). This calculation is robust in
22+
// the event of integer overflow.
2723
func Sub[N constraints.Integer](min N, max N, a N, b N) (N, bool) {
24+
if (a < min) || (a > max) || (b < min) || (b > max) { return 0, false }
25+
if (min > max) || (max < min) { return 0, false }
2826
if (b < 0) && (a > (max + b)) { return 0, false }
2927
if (b > 0) && (a < (min + b)) { return 0, false }
3028
return a - b, true
3129
}
3230

33-
// Mul returns (a * b, true) iff the result lies between min and max inclusive,
34-
// otherwise returns (0, false). This calculation is robust in the event of
35-
// integer overflow.
36-
//
37-
// The input arguments must satisfy the inequalities: `min <= a <= max` and
38-
// `min <= b <= max`.
31+
// Mul returns (a * b, true) iff a, b, and the result all lie between min and
32+
// max inclusive, otherwise returns (0, false). This calculation is robust in
33+
// the event of integer overflow.
3934
func Mul[N constraints.Integer](min N, max N, a N, b N) (N, bool) {
40-
if (a == 0) || (b == 0) { return 0, true }
35+
if (a < min) || (a > max) || (b < min) || (b > max) { return 0, false }
36+
if (min > max) || (max < min) { return 0, false }
4137

4238
x := a * b
4339
if (x < min) || (x > max) { return 0, false }
44-
if (a != x/b) { return 0, false }
40+
if (x != 0) && (a != x/b) { return 0, false }
4541
return x, true
4642
}
4743

48-
// Abs returns (-i, true) for i < 0, or (i, true) for i >= 0 iff the result lies
49-
// between min and max inclusive. Otherwise returns (0, false).
50-
//
51-
// The input arguments must satisfy the inequality `min <= i <= max`.
44+
// Abs returns (positive i, true) iff both i and the result lie between min and
45+
// max inclusive. Otherwise, returns (0, false).
5246
func Abs[N constraints.Integer](min N, max N, i N) (N, bool) {
53-
if (i >= 0) { return i, true }
54-
return Sub(min, max, 0, i)
47+
if (i < min) || (i > max) { return 0, false }
48+
if (min > max) || (max < min) { return 0, false }
49+
if (i < 0) && (0 > (max + i)) { return 0, false }
50+
if (i > 0) && (0 < (min + i)) { return 0, false }
51+
if i < 0 { return -i, true } else { return i, true }
5552
}
5653

57-
// Inv returns (-i) iff the result lies between min and max inclusive.
58-
// Otherwise returns (0, false).
59-
//
60-
// The input arguments must satisfy the inequality `min <= i <= max`.
54+
// Inv returns (-i, true) iff both i and the result lie between min and max
55+
// inclusive. Otherwise, returns (0, false).
6156
func Inv[N constraints.Integer](min N, max N, i N) (N, bool) {
62-
return Sub(min, max, 0, i)
57+
if (i < min) || (i > max) { return 0, false }
58+
if (min > max) || (max < min) { return 0, false }
59+
if (i < 0) && (0 > (max + i)) { return 0, false }
60+
if (i > 0) && (0 < (min + i)) { return 0, false }
61+
return -i, true
6362
}

operator/checked/limits.go

+21-13
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import (
77
"golang.org/x/exp/constraints"
88
)
99

10-
// Limits provides a convenient way to fill the min and max arguments to the
11-
// checked operator functions. The inequality Min <= Max must be satisfied.
10+
// Limits are a pair of integer values defining a range between an (inclusive)
11+
// minimum value and an (inclusive) maximum value.
1212
type Limits[I constraints.Integer] struct {
1313
Min I
1414
Max I
1515
}
1616

17-
// GetLimits returns a filled-in [Limit] for the given integer type.
17+
// GetLimits returns a filled-in [Limit] representing the widest possible
18+
// minimum and maximum values for a generic integer type.
1819
func GetLimits[I constraints.Integer]() Limits[I] {
1920
var n Limits[I]
2021
switch x := any(&n).(type) {
@@ -36,6 +37,10 @@ func GetLimits[I constraints.Integer]() Limits[I] {
3637

3738
// Filled-in [Limits] about different integer types with minimum and maximum
3839
// set to the largest range supported by the limit.
40+
//
41+
// For signed integers, these are the appropriately sized math.MinInt and
42+
// math.MaxInt constants. For unsigned integers, these are zero and the
43+
// appropriately sized math.MaxUint constants.
3944
var (
4045
Int = Limits[int] {math.MinInt, math.MaxInt}
4146
Int8 = Limits[int8] {math.MinInt8, math.MaxInt8}
@@ -50,32 +55,35 @@ var (
5055
Uint64 = Limits[uint64]{0, math.MaxUint64}
5156
)
5257

53-
// Add calls [checked.Add] with min and max filled in with the associated
54-
// [Limits] values.
58+
// Add returns (a + b, true) iff a, b, and the result all lie between the Limit
59+
// min and max inclusive, otherwise returns (0, false). This calculation is
60+
// robust in the event of integer overflow.
5561
func (l Limits[I]) Add(a I, b I) (I, bool) {
5662
return Add(l.Min, l.Max, a, b)
5763
}
5864

59-
// Sub calls [checked.Sub] with min and max filled in with the associated
60-
// [Limits] values.
65+
// Sub returns (a - b, true) iff a, b, and the result all lie between the Limit
66+
// min and max inclusive, otherwise returns (0, false). This calculation is
67+
// robust in the event of integer overflow.
6168
func (l Limits[I]) Sub(a I, b I) (I, bool) {
6269
return Sub(l.Min, l.Max, a, b)
6370
}
6471

65-
// Mul calls [checked.Mul] with min and max filled in with the associated
66-
// [Limits] values.
72+
// Mul returns (a * b, true) iff a, b, and the result all lie between the Limit
73+
// min and max inclusive, otherwise returns (0, false). This calculation is
74+
// robust in the event of integer overflow.
6775
func (l Limits[I]) Mul(a I, b I) (I, bool) {
6876
return Mul(l.Min, l.Max, a, b)
6977
}
7078

71-
// Abs calls [checked.Abs] with min and max filled in with the associated
72-
// [Limits] values.
79+
// Abs returns (positive i, true) iff both i and the result lie between the
80+
// Limit min and max inclusive. Otherwise, returns (0, false).
7381
func (l Limits[I]) Abs(i I) (I, bool) {
7482
return Abs(l.Min, l.Max, i)
7583
}
7684

77-
// Inv calls [checked.Inv] with min and max filled in with the associated
78-
// [Limits] values.
85+
// Inv returns (-i, true) iff both i and the result lie between the Limit min
86+
// and max inclusive. Otherwise, returns (0, false).
7987
func (l Limits[I]) Inv(i I) (I, bool) {
8088
return Inv(l.Min, l.Max, i)
8189
}

0 commit comments

Comments
 (0)