Skip to content

Commit 09b38aa

Browse files
authored
Merge pull request #8653 from ProofOfKeags/fn-prim
DynComms [0/n]: `fn` package additions
2 parents f464dac + eaa5e4a commit 09b38aa

File tree

14 files changed

+1586
-65
lines changed

14 files changed

+1586
-65
lines changed

fn/conc_queue.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package fn
22

33
import (
44
"sync"
5-
6-
"github.com/lightninglabs/neutrino/cache/lru"
75
)
86

97
// ConcurrentQueue is a typed concurrent-safe FIFO queue with unbounded
@@ -17,21 +15,21 @@ type ConcurrentQueue[T any] struct {
1715

1816
chanIn chan T
1917
chanOut chan T
20-
overflow *lru.List[T]
18+
overflow *List[T]
2119

2220
wg sync.WaitGroup
2321
quit chan struct{}
2422
}
2523

2624
// NewConcurrentQueue constructs a ConcurrentQueue. The bufferSize parameter is
2725
// the capacity of the output channel. When the size of the queue is below this
28-
// threshold, pushes do n[?12;4$yot incur the overhead of the less efficient overflow
26+
// threshold, pushes do not incur the overhead of the less efficient overflow
2927
// structure.
3028
func NewConcurrentQueue[T any](bufferSize int) *ConcurrentQueue[T] {
3129
return &ConcurrentQueue[T]{
3230
chanIn: make(chan T),
3331
chanOut: make(chan T, bufferSize),
34-
overflow: lru.NewList[T](),
32+
overflow: NewList[T](),
3533
quit: make(chan struct{}),
3634
}
3735
}

fn/either.go

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,136 @@ package fn
22

33
// Either is a type that can be either left or right.
44
type Either[L any, R any] struct {
5-
left Option[L]
6-
right Option[R]
5+
isRight bool
6+
left L
7+
right R
78
}
89

910
// NewLeft returns an Either with a left value.
1011
func NewLeft[L any, R any](l L) Either[L, R] {
11-
return Either[L, R]{left: Some(l), right: None[R]()}
12+
return Either[L, R]{left: l}
1213
}
1314

1415
// NewRight returns an Either with a right value.
1516
func NewRight[L any, R any](r R) Either[L, R] {
16-
return Either[L, R]{left: None[L](), right: Some(r)}
17+
return Either[L, R]{isRight: true, right: r}
18+
}
19+
20+
// ElimEither is the universal Either eliminator. It can be used to safely
21+
// handle all possible values inside the Either by supplying two continuations,
22+
// one for each side of the Either.
23+
func ElimEither[L, R, O any](f func(L) O, g func(R) O, e Either[L, R]) O {
24+
if !e.isRight {
25+
return f(e.left)
26+
}
27+
28+
return g(e.right)
1729
}
1830

1931
// WhenLeft executes the given function if the Either is left.
2032
func (e Either[L, R]) WhenLeft(f func(L)) {
21-
e.left.WhenSome(f)
33+
if !e.isRight {
34+
f(e.left)
35+
}
2236
}
2337

2438
// WhenRight executes the given function if the Either is right.
2539
func (e Either[L, R]) WhenRight(f func(R)) {
26-
e.right.WhenSome(f)
40+
if e.isRight {
41+
f(e.right)
42+
}
2743
}
2844

2945
// IsLeft returns true if the Either is left.
3046
func (e Either[L, R]) IsLeft() bool {
31-
return e.left.IsSome()
47+
return !e.isRight
3248
}
3349

3450
// IsRight returns true if the Either is right.
3551
func (e Either[L, R]) IsRight() bool {
36-
return e.right.IsSome()
52+
return e.isRight
53+
}
54+
55+
// LeftToOption converts a Left value to an Option, returning None if the inner
56+
// Either value is a Right value.
57+
func (e Either[L, R]) LeftToOption() Option[L] {
58+
if e.isRight {
59+
return None[L]()
60+
}
61+
62+
return Some(e.left)
63+
}
64+
65+
// RightToOption converts a Right value to an Option, returning None if the
66+
// inner Either value is a Left value.
67+
func (e Either[L, R]) RightToOption() Option[R] {
68+
if !e.isRight {
69+
return None[R]()
70+
}
71+
72+
return Some(e.right)
73+
}
74+
75+
// UnwrapLeftOr will extract the Left value from the Either if it is present
76+
// returning the supplied default if it is not.
77+
func (e Either[L, R]) UnwrapLeftOr(l L) L {
78+
if e.isRight {
79+
return l
80+
}
81+
82+
return e.left
83+
}
84+
85+
// UnwrapRightOr will extract the Right value from the Either if it is present
86+
// returning the supplied default if it is not.
87+
func (e Either[L, R]) UnwrapRightOr(r R) R {
88+
if !e.isRight {
89+
return r
90+
}
91+
92+
return e.right
93+
}
94+
95+
// Swap reverses the type argument order. This can be useful as an adapter
96+
// between APIs.
97+
func (e Either[L, R]) Swap() Either[R, L] {
98+
return Either[R, L]{
99+
isRight: !e.isRight,
100+
left: e.right,
101+
right: e.left,
102+
}
37103
}
38104

39105
// MapLeft maps the left value of the Either to a new value.
40-
func MapLeft[L any, R any, O any](f func(L) O) func(Either[L, R]) Option[O] {
41-
return func(e Either[L, R]) Option[O] {
42-
if e.IsLeft() {
43-
return MapOption(f)(e.left)
106+
func MapLeft[L, R, O any](f func(L) O) func(Either[L, R]) Either[O, R] {
107+
return func(e Either[L, R]) Either[O, R] {
108+
if !e.isRight {
109+
return Either[O, R]{
110+
isRight: false,
111+
left: f(e.left),
112+
}
113+
}
114+
115+
return Either[O, R]{
116+
isRight: true,
117+
right: e.right,
118+
}
119+
}
120+
}
121+
122+
// MapRight maps the right value of the Either to a new value.
123+
func MapRight[L, R, O any](f func(R) O) func(Either[L, R]) Either[L, O] {
124+
return func(e Either[L, R]) Either[L, O] {
125+
if e.isRight {
126+
return Either[L, O]{
127+
isRight: true,
128+
right: f(e.right),
129+
}
44130
}
45131

46-
return None[O]()
132+
return Either[L, O]{
133+
isRight: false,
134+
left: e.left,
135+
}
47136
}
48137
}

fn/either_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package fn
2+
3+
import (
4+
"testing"
5+
"testing/quick"
6+
)
7+
8+
func TestPropConstructorEliminatorDuality(t *testing.T) {
9+
f := func(i int, s string, isRight bool) bool {
10+
Len := func(s string) int { return len(s) } // smh
11+
if isRight {
12+
v := ElimEither(
13+
Iden[int],
14+
Len,
15+
NewRight[int, string](s),
16+
)
17+
return v == Len(s)
18+
}
19+
20+
v := ElimEither(
21+
Iden[int],
22+
Len,
23+
NewLeft[int, string](i),
24+
)
25+
return v == i
26+
}
27+
if err := quick.Check(f, nil); err != nil {
28+
t.Fatal(err)
29+
}
30+
}
31+
32+
func TestPropWhenClauseExclusivity(t *testing.T) {
33+
f := func(i int, isRight bool) bool {
34+
var e Either[int, int]
35+
if isRight {
36+
e = NewRight[int, int](i)
37+
} else {
38+
e = NewLeft[int, int](i)
39+
}
40+
z := 0
41+
e.WhenLeft(func(x int) { z += x })
42+
e.WhenRight(func(x int) { z += x })
43+
44+
return z != 2*i && e.IsLeft() != e.IsRight()
45+
}
46+
if err := quick.Check(f, nil); err != nil {
47+
t.Fatal(err)
48+
}
49+
}
50+
51+
func TestPropSwapEitherSelfInverting(t *testing.T) {
52+
f := func(i int, s string, isRight bool) bool {
53+
var e Either[int, string]
54+
if isRight {
55+
e = NewRight[int, string](s)
56+
} else {
57+
e = NewLeft[int, string](i)
58+
}
59+
return e.Swap().Swap() == e
60+
}
61+
if err := quick.Check(f, nil); err != nil {
62+
t.Fatal(err)
63+
}
64+
}
65+
66+
func TestPropMapLeftIdentity(t *testing.T) {
67+
f := func(i int, s string, isRight bool) bool {
68+
var e Either[int, string]
69+
if isRight {
70+
e = NewRight[int, string](s)
71+
} else {
72+
e = NewLeft[int, string](i)
73+
}
74+
return MapLeft[int, string, int](Iden[int])(e) == e
75+
}
76+
if err := quick.Check(f, nil); err != nil {
77+
t.Fatal(err)
78+
}
79+
}
80+
81+
func TestPropMapRightIdentity(t *testing.T) {
82+
f := func(i int, s string, isRight bool) bool {
83+
var e Either[int, string]
84+
if isRight {
85+
e = NewRight[int, string](s)
86+
} else {
87+
e = NewLeft[int, string](i)
88+
}
89+
return MapRight[int, string, string](Iden[string])(e) == e
90+
}
91+
if err := quick.Check(f, nil); err != nil {
92+
t.Fatal(err)
93+
}
94+
}
95+
96+
func TestPropToOptionIdentities(t *testing.T) {
97+
f := func(i int, s string, isRight bool) bool {
98+
var e Either[int, string]
99+
if isRight {
100+
e = NewRight[int, string](s)
101+
102+
r2O := e.RightToOption() == Some(s)
103+
o2R := e == OptionToRight[string, int, string](
104+
Some(s), i,
105+
)
106+
l2O := e.LeftToOption() == None[int]()
107+
108+
return r2O && o2R && l2O
109+
} else {
110+
e = NewLeft[int, string](i)
111+
l2O := e.LeftToOption() == Some(i)
112+
o2L := e == OptionToLeft[int, int](Some(i), s)
113+
r2O := e.RightToOption() == None[string]()
114+
115+
return l2O && o2L && r2O
116+
}
117+
}
118+
if err := quick.Check(f, nil); err != nil {
119+
t.Fatal(err)
120+
}
121+
}
122+
123+
func TestPropUnwrapIdentities(t *testing.T) {
124+
f := func(i int, s string, isRight bool) bool {
125+
var e Either[int, string]
126+
if isRight {
127+
e = NewRight[int, string](s)
128+
return e.UnwrapRightOr("") == s &&
129+
e.UnwrapLeftOr(0) == 0
130+
} else {
131+
e = NewLeft[int, string](i)
132+
return e.UnwrapLeftOr(0) == i &&
133+
e.UnwrapRightOr("") == ""
134+
}
135+
}
136+
if err := quick.Check(f, nil); err != nil {
137+
t.Fatal(err)
138+
}
139+
}

fn/fn.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package fn
2+
3+
// Unit is a type alias for the empty struct to make it a bit less noisy to
4+
// communicate the informationaless type.
5+
type Unit = struct{}
6+
7+
// Comp is left to right function composition. Comp(f, g)(x) == g(f(x)). This
8+
// can make it easier to create on the fly closures that we may use as
9+
// arguments to other functions defined in this package (or otherwise).
10+
func Comp[A, B, C any](f func(A) B, g func(B) C) func(A) C {
11+
return func(a A) C {
12+
return g(f(a))
13+
}
14+
}
15+
16+
// Iden is the left and right identity of Comp. It is a function that simply
17+
// returns its argument. The utility of this function is only apparent in
18+
// conjunction with other functions in this package.
19+
func Iden[A any](a A) A {
20+
return a
21+
}
22+
23+
// Const is a function that accepts an argument and returns a function that
24+
// always returns that value irrespective of the returned function's argument.
25+
// This is also quite useful in conjunction with higher order functions.
26+
func Const[A, B any](a A) func(B) A {
27+
return func(_ B) A {
28+
return a
29+
}
30+
}
31+
32+
// Eq is a curried function that returns true if its eventual two arguments are
33+
// equal.
34+
func Eq[A comparable](x A) func(A) bool {
35+
return func(y A) bool {
36+
return x == y
37+
}
38+
}
39+
40+
// Neq is a curried function that returns true if its eventual two arguments are
41+
// not equal.
42+
func Neq[A comparable](x A) func(A) bool {
43+
return func(y A) bool {
44+
return x != y
45+
}
46+
}

fn/func.go

Lines changed: 0 additions & 17 deletions
This file was deleted.

fn/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ module github.com/lightningnetwork/lnd/fn
33
go 1.19
44

55
require (
6-
github.com/lightninglabs/neutrino/cache v1.1.2
76
github.com/stretchr/testify v1.8.1
87
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b
8+
golang.org/x/sync v0.7.0
99
)
1010

1111
require (

0 commit comments

Comments
 (0)