-
Notifications
You must be signed in to change notification settings - Fork 2.3k
DynComms [0/n]: fn package additions
#8653
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
94acbe9
5902aa5
c3603cc
1dd56f1
c4df2f1
9c30bce
fb1437c
364d79e
fa2e25d
eaa5e4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,47 +2,136 @@ package fn | |
|
|
||
| // Either is a type that can be either left or right. | ||
| type Either[L any, R any] struct { | ||
| left Option[L] | ||
| right Option[R] | ||
| isRight bool | ||
| left L | ||
| right R | ||
| } | ||
|
|
||
| // NewLeft returns an Either with a left value. | ||
| func NewLeft[L any, R any](l L) Either[L, R] { | ||
| return Either[L, R]{left: Some(l), right: None[R]()} | ||
| return Either[L, R]{left: l} | ||
| } | ||
|
|
||
| // NewRight returns an Either with a right value. | ||
| func NewRight[L any, R any](r R) Either[L, R] { | ||
| return Either[L, R]{left: None[L](), right: Some(r)} | ||
| return Either[L, R]{isRight: true, right: r} | ||
| } | ||
|
|
||
| // ElimEither is the universal Either eliminator. It can be used to safely | ||
| // handle all possible values inside the Either by supplying two continuations, | ||
| // one for each side of the Either. | ||
| func ElimEither[L, R, O any](f func(L) O, g func(R) O, e Either[L, R]) O { | ||
| if !e.isRight { | ||
|
ProofOfKeags marked this conversation as resolved.
Outdated
|
||
| return f(e.left) | ||
| } | ||
|
|
||
| return g(e.right) | ||
| } | ||
|
|
||
| // WhenLeft executes the given function if the Either is left. | ||
| func (e Either[L, R]) WhenLeft(f func(L)) { | ||
| e.left.WhenSome(f) | ||
| if !e.isRight { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So there's no feedback when calling these
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. This function should only be used if we want to conditionally run actions using the inner value on one side. If you need to somehow introspect this value to understand whether the closure was run or not, it's probably better to use |
||
| f(e.left) | ||
| } | ||
| } | ||
|
|
||
| // WhenRight executes the given function if the Either is right. | ||
| func (e Either[L, R]) WhenRight(f func(R)) { | ||
| e.right.WhenSome(f) | ||
| if e.isRight { | ||
| f(e.right) | ||
| } | ||
| } | ||
|
|
||
| // IsLeft returns true if the Either is left. | ||
| func (e Either[L, R]) IsLeft() bool { | ||
| return e.left.IsSome() | ||
| return !e.isRight | ||
| } | ||
|
|
||
| // IsRight returns true if the Either is right. | ||
| func (e Either[L, R]) IsRight() bool { | ||
| return e.right.IsSome() | ||
| return e.isRight | ||
| } | ||
|
|
||
| // LeftToOption converts a Left value to an Option, returning None if the inner | ||
| // Either value is a Right value. | ||
| func (e Either[L, R]) LeftToOption() Option[L] { | ||
| if e.isRight { | ||
| return None[L]() | ||
| } | ||
|
|
||
| return Some(e.left) | ||
| } | ||
|
|
||
| // RightToOption converts a Right value to an Option, returning None if the | ||
| // inner Either value is a Left value. | ||
| func (e Either[L, R]) RightToOption() Option[R] { | ||
| if !e.isRight { | ||
| return None[R]() | ||
| } | ||
|
|
||
| return Some(e.right) | ||
| } | ||
|
|
||
| // UnwrapLeftOr will extract the Left value from the Either if it is present | ||
| // returning the supplied default if it is not. | ||
| func (e Either[L, R]) UnwrapLeftOr(l L) L { | ||
| if e.isRight { | ||
| return l | ||
| } | ||
|
|
||
| return e.left | ||
| } | ||
|
|
||
| // UnwrapRightOr will extract the Right value from the Either if it is present | ||
| // returning the supplied default if it is not. | ||
| func (e Either[L, R]) UnwrapRightOr(r R) R { | ||
| if !e.isRight { | ||
| return r | ||
| } | ||
|
|
||
| return e.right | ||
| } | ||
|
|
||
| // Swap reverses the type argument order. This can be useful as an adapter | ||
| // between APIs. | ||
| func (e Either[L, R]) Swap() Either[R, L] { | ||
| return Either[R, L]{ | ||
| isRight: !e.isRight, | ||
| left: e.right, | ||
| right: e.left, | ||
| } | ||
| } | ||
|
|
||
| // MapLeft maps the left value of the Either to a new value. | ||
| func MapLeft[L any, R any, O any](f func(L) O) func(Either[L, R]) Option[O] { | ||
| return func(e Either[L, R]) Option[O] { | ||
| if e.IsLeft() { | ||
| return MapOption(f)(e.left) | ||
| func MapLeft[L, R, O any](f func(L) O) func(Either[L, R]) Either[O, R] { | ||
| return func(e Either[L, R]) Either[O, R] { | ||
| if !e.isRight { | ||
| return Either[O, R]{ | ||
| isRight: false, | ||
| left: f(e.left), | ||
| } | ||
| } | ||
|
|
||
| return Either[O, R]{ | ||
| isRight: true, | ||
| right: e.right, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // MapRight maps the right value of the Either to a new value. | ||
| func MapRight[L, R, O any](f func(R) O) func(Either[L, R]) Either[L, O] { | ||
| return func(e Either[L, R]) Either[L, O] { | ||
| if e.isRight { | ||
| return Either[L, O]{ | ||
| isRight: true, | ||
| right: f(e.right), | ||
| } | ||
| } | ||
|
|
||
| return None[O]() | ||
| return Either[L, O]{ | ||
| isRight: false, | ||
| left: e.left, | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| package fn | ||
|
ProofOfKeags marked this conversation as resolved.
Outdated
|
||
|
|
||
| import ( | ||
| "testing" | ||
| "testing/quick" | ||
| ) | ||
|
|
||
| func TestPropConstructorEliminatorDuality(t *testing.T) { | ||
| f := func(i int, s string, isRight bool) bool { | ||
| Len := func(s string) int { return len(s) } // smh | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Q: what does the comment mean smh ?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Shake my head", means disappointment. I was making some commentary about how I couldn't just put |
||
| if isRight { | ||
| v := ElimEither( | ||
| Iden[int], | ||
| Len, | ||
| NewRight[int, string](s), | ||
| ) | ||
| return v == Len(s) | ||
| } | ||
|
|
||
| v := ElimEither( | ||
| Iden[int], | ||
| Len, | ||
| NewLeft[int, string](i), | ||
| ) | ||
| return v == i | ||
| } | ||
| if err := quick.Check(f, nil); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| } | ||
|
|
||
| func TestPropWhenClauseExclusivity(t *testing.T) { | ||
| f := func(i int, isRight bool) bool { | ||
| var e Either[int, int] | ||
| if isRight { | ||
| e = NewRight[int, int](i) | ||
| } else { | ||
| e = NewLeft[int, int](i) | ||
| } | ||
| z := 0 | ||
| e.WhenLeft(func(x int) { z += x }) | ||
| e.WhenRight(func(x int) { z += x }) | ||
|
|
||
| return z != 2*i && e.IsLeft() != e.IsRight() | ||
| } | ||
| if err := quick.Check(f, nil); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| } | ||
|
|
||
| func TestPropSwapEitherSelfInverting(t *testing.T) { | ||
| f := func(i int, s string, isRight bool) bool { | ||
| var e Either[int, string] | ||
| if isRight { | ||
| e = NewRight[int, string](s) | ||
| } else { | ||
| e = NewLeft[int, string](i) | ||
| } | ||
| return e.Swap().Swap() == e | ||
| } | ||
| if err := quick.Check(f, nil); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| } | ||
|
|
||
| func TestPropMapLeftIdentity(t *testing.T) { | ||
| f := func(i int, s string, isRight bool) bool { | ||
| var e Either[int, string] | ||
| if isRight { | ||
| e = NewRight[int, string](s) | ||
| } else { | ||
| e = NewLeft[int, string](i) | ||
| } | ||
| return MapLeft[int, string, int](Iden[int])(e) == e | ||
| } | ||
| if err := quick.Check(f, nil); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| } | ||
|
|
||
| func TestPropMapRightIdentity(t *testing.T) { | ||
| f := func(i int, s string, isRight bool) bool { | ||
| var e Either[int, string] | ||
| if isRight { | ||
| e = NewRight[int, string](s) | ||
| } else { | ||
| e = NewLeft[int, string](i) | ||
| } | ||
| return MapRight[int, string, string](Iden[string])(e) == e | ||
| } | ||
| if err := quick.Check(f, nil); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| } | ||
|
|
||
| func TestPropToOptionIdentities(t *testing.T) { | ||
| f := func(i int, s string, isRight bool) bool { | ||
| var e Either[int, string] | ||
| if isRight { | ||
| e = NewRight[int, string](s) | ||
|
|
||
| r2O := e.RightToOption() == Some(s) | ||
| o2R := e == OptionToRight[string, int, string]( | ||
| Some(s), i, | ||
| ) | ||
| l2O := e.LeftToOption() == None[int]() | ||
|
|
||
| return r2O && o2R && l2O | ||
| } else { | ||
| e = NewLeft[int, string](i) | ||
| l2O := e.LeftToOption() == Some(i) | ||
| o2L := e == OptionToLeft[int, int](Some(i), s) | ||
| r2O := e.RightToOption() == None[string]() | ||
|
|
||
| return l2O && o2L && r2O | ||
| } | ||
| } | ||
| if err := quick.Check(f, nil); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| } | ||
|
|
||
| func TestPropUnwrapIdentities(t *testing.T) { | ||
| f := func(i int, s string, isRight bool) bool { | ||
| var e Either[int, string] | ||
| if isRight { | ||
| e = NewRight[int, string](s) | ||
| return e.UnwrapRightOr("") == s && | ||
| e.UnwrapLeftOr(0) == 0 | ||
| } else { | ||
| e = NewLeft[int, string](i) | ||
| return e.UnwrapLeftOr(0) == i && | ||
| e.UnwrapRightOr("") == "" | ||
| } | ||
| } | ||
| if err := quick.Check(f, nil); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| package fn | ||
|
|
||
| // Unit is a type alias for the empty struct to make it a bit less noisy to | ||
| // communicate the informationaless type. | ||
| type Unit = struct{} | ||
|
|
||
| // Comp is left to right function composition. Comp(f, g)(x) == g(f(x)). This | ||
| // can make it easier to create on the fly closures that we may use as | ||
| // arguments to other functions defined in this package (or otherwise). | ||
| func Comp[A, B, C any](f func(A) B, g func(B) C) func(A) C { | ||
| return func(a A) C { | ||
| return g(f(a)) | ||
| } | ||
| } | ||
|
|
||
| // Iden is the left and right identity of Comp. It is a function that simply | ||
| // returns its argument. The utility of this function is only apparent in | ||
| // conjunction with other functions in this package. | ||
| func Iden[A any](a A) A { | ||
| return a | ||
| } | ||
|
|
||
| // Const is a function that accepts an argument and returns a function that | ||
| // always returns that value irrespective of the returned function's argument. | ||
| // This is also quite useful in conjunction with higher order functions. | ||
| func Const[A, B any](a A) func(B) A { | ||
| return func(_ B) A { | ||
| return a | ||
| } | ||
| } | ||
|
|
||
| // Eq is a curried function that returns true if its eventual two arguments are | ||
| // equal. | ||
| func Eq[A comparable](x A) func(A) bool { | ||
| return func(y A) bool { | ||
| return x == y | ||
| } | ||
| } | ||
|
|
||
| // Neq is a curried function that returns true if its eventual two arguments are | ||
| // not equal. | ||
| func Neq[A comparable](x A) func(A) bool { | ||
| return func(y A) bool { | ||
| return x != y | ||
| } | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,9 +3,9 @@ module github.com/lightningnetwork/lnd/fn | |
| go 1.19 | ||
|
|
||
| require ( | ||
| github.com/lightninglabs/neutrino/cache v1.1.2 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔥 |
||
| github.com/stretchr/testify v1.8.1 | ||
| golang.org/x/exp v0.0.0-20231226003508-02704c960a9b | ||
| golang.org/x/sync v0.7.0 | ||
| ) | ||
|
|
||
| require ( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.