From 89f8396ccdf2bedf4b5035034d8d3f0e180d872f Mon Sep 17 00:00:00 2001 From: Force4760 Date: Tue, 9 Aug 2022 11:01:41 +0100 Subject: [PATCH 1/7] Implemnt a Tuple and basic destructuring funcs --- pair/pair.go | 22 ++++++++++++++++++++++ pair/pair_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 pair/pair.go create mode 100644 pair/pair_test.go diff --git a/pair/pair.go b/pair/pair.go new file mode 100644 index 0000000..9d158d6 --- /dev/null +++ b/pair/pair.go @@ -0,0 +1,22 @@ +package pair + +type Tuple[A, B any] struct { + a A + b B +} + +func Pair[A, B any](a A, b B) Tuple[A, B] { + return Tuple[A, B]{a, b} +} + +func Fst[A, B any](t Tuple[A, B]) A { + return t.a +} + +func Snd[A, B any](t Tuple[A, B]) B { + return t.b +} + +func Get[A, B any](t Tuple[A, B]) (A, B) { + return t.a, t.b +} diff --git a/pair/pair_test.go b/pair/pair_test.go new file mode 100644 index 0000000..f727693 --- /dev/null +++ b/pair/pair_test.go @@ -0,0 +1,37 @@ +package pair + +import "testing" + +func TestPair(t *testing.T) { + res := Pair(42, "42") + if res.a != 42 { + t.Error("Pair should return a struct with 42 as the first value. Received:", res.a) + } + if res.b != "42" { + t.Error("Pair should return a struct with \"42\" as the second value. Received:", res.b) + } +} + +func TestFst(t *testing.T) { + res := Fst(Pair(42, "42")) + if res != 42 { + t.Error("Fst should return 42. Received:", res) + } +} + +func TestSnd(t *testing.T) { + res := Snd(Pair(42, "42")) + if res != "42" { + t.Error("Fst should return \"42\". Received:", res) + } +} + +func TestGet(t *testing.T) { + res1, res2 := Get(Pair(42, "42")) + if res1 != 42 { + t.Error("Get should return 42 as the first value. Received:", res1) + } + if res2 != "42" { + t.Error("Get should return \"42\" as the second value. Received:", res2) + } +} From e645c6666f375382b2d288d98b00fe1efeb7885e Mon Sep 17 00:00:00 2001 From: Force4760 Date: Tue, 9 Aug 2022 11:12:07 +0100 Subject: [PATCH 2/7] Implement and Test Maps for the Tuple type --- pair/pair.go | 26 ++++++++++++++++++++++++++ pair/pair_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/pair/pair.go b/pair/pair.go index 9d158d6..6ddb5a5 100644 --- a/pair/pair.go +++ b/pair/pair.go @@ -1,22 +1,48 @@ package pair +// Base Tuple struct type Tuple[A, B any] struct { a A b B } +// Constructor for a Tuple func Pair[A, B any](a A, b B) Tuple[A, B] { return Tuple[A, B]{a, b} } +// Getter for the first value of the Tuple func Fst[A, B any](t Tuple[A, B]) A { return t.a } +// Getter for the second value of the Tuple func Snd[A, B any](t Tuple[A, B]) B { return t.b } +// Getter for both values of the Tuple func Get[A, B any](t Tuple[A, B]) (A, B) { return t.a, t.b } + +// Execute the function on the first value of the Tuple +func MapFst[A, B, R any](fn func(A) R) func(Tuple[A, B]) Tuple[R, B] { + return func(t Tuple[A, B]) Tuple[R, B] { + return Tuple[R, B]{fn(t.a), t.b} + } +} + +// Execute the function on the second value of the Tuple +func MapSnd[A, B, R any](fn func(B) R) func(Tuple[A, B]) Tuple[A, R] { + return func(t Tuple[A, B]) Tuple[A, R] { + return Tuple[A, R]{t.a, fn(t.b)} + } +} + +// Execute the functions on both the first and second values of the Tuple +func MapBoth[A, B, R, S any](fnF func(A) R, fnS func(B) S) func(Tuple[A, B]) Tuple[R, S] { + return func(t Tuple[A, B]) Tuple[R, S] { + return Tuple[R, S]{fnF(t.a), fnS(t.b)} + } +} diff --git a/pair/pair_test.go b/pair/pair_test.go index f727693..ec5ef4b 100644 --- a/pair/pair_test.go +++ b/pair/pair_test.go @@ -35,3 +35,37 @@ func TestGet(t *testing.T) { t.Error("Get should return \"42\" as the second value. Received:", res2) } } + +func TestMapFst(t *testing.T) { + res := MapFst[int, string](func(x int) int { return x + 1 })(Pair(41, "42")) + if Fst(res) != 42 { + t.Error("MapFirst should return 42 as a first value. Received:", Fst(res)) + } + if Snd(res) != "42" { + t.Error("MapFst should leave the second value \"42\" alone. Received:", Snd(res)) + } +} + +func TestMapSnd(t *testing.T) { + res := MapSnd[string, int](func(x int) int { return x + 1 })(Pair("42", 41)) + if Fst(res) != "42" { + t.Error("MapSnd should leave the first value \"42\" alone. Received:", Fst(res)) + } + if Snd(res) != 42 { + t.Error("MapFirst should return 42 as a second value. Received:", Snd(res)) + } +} + +func TestMapBoth(t *testing.T) { + res := MapBoth( + func(x bool) bool { return !x }, + func(x int) int { return x + 1 }, + )(Pair(false, 41)) + + if Fst(res) != true { + t.Error("MapBoth should return true as the first value. Received:", Fst(res)) + } + if Snd(res) != 42 { + t.Error("MapBoth should return 42 as the second value. Received:", Snd(res)) + } +} From ed66b9bbb9695d6c4b4010266ece4c865631a7c3 Mon Sep 17 00:00:00 2001 From: Force4760 Date: Tue, 9 Aug 2022 11:24:54 +0100 Subject: [PATCH 3/7] Implement and test checkers --- pair/pair.go | 23 +++++++++++++- pair/pair_test.go | 76 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/pair/pair.go b/pair/pair.go index 6ddb5a5..971c985 100644 --- a/pair/pair.go +++ b/pair/pair.go @@ -27,7 +27,7 @@ func Get[A, B any](t Tuple[A, B]) (A, B) { } // Execute the function on the first value of the Tuple -func MapFst[A, B, R any](fn func(A) R) func(Tuple[A, B]) Tuple[R, B] { +func MapFst[B, A, R any](fn func(A) R) func(Tuple[A, B]) Tuple[R, B] { return func(t Tuple[A, B]) Tuple[R, B] { return Tuple[R, B]{fn(t.a), t.b} } @@ -46,3 +46,24 @@ func MapBoth[A, B, R, S any](fnF func(A) R, fnS func(B) S) func(Tuple[A, B]) Tup return Tuple[R, S]{fnF(t.a), fnS(t.b)} } } + +// Helper to chcek if the first value of the Tuple satisfies a predicate +func CheckFst[B, A any](fn func(A) bool) func(Tuple[A, B]) bool { + return func(t Tuple[A, B]) bool { + return fn(t.a) + } +} + +// Helper to chcek if the second value of the Tuple satisfies a predicate +func CheckSnd[A, B any](fn func(B) bool) func(Tuple[A, B]) bool { + return func(t Tuple[A, B]) bool { + return fn(t.b) + } +} + +// Helper to chcek if both the first and the second values of the Tuple satisfies their respective predicate +func CheckBoth[A, B any](fnF func(A) bool, fnS func(B) bool) func(Tuple[A, B]) bool { + return func(t Tuple[A, B]) bool { + return fnF(t.a) && fnS(t.b) + } +} diff --git a/pair/pair_test.go b/pair/pair_test.go index ec5ef4b..5083516 100644 --- a/pair/pair_test.go +++ b/pair/pair_test.go @@ -37,7 +37,7 @@ func TestGet(t *testing.T) { } func TestMapFst(t *testing.T) { - res := MapFst[int, string](func(x int) int { return x + 1 })(Pair(41, "42")) + res := MapFst[string](func(x int) int { return x + 1 })(Pair(41, "42")) if Fst(res) != 42 { t.Error("MapFirst should return 42 as a first value. Received:", Fst(res)) } @@ -47,7 +47,7 @@ func TestMapFst(t *testing.T) { } func TestMapSnd(t *testing.T) { - res := MapSnd[string, int](func(x int) int { return x + 1 })(Pair("42", 41)) + res := MapSnd[string](func(x int) int { return x + 1 })(Pair("42", 41)) if Fst(res) != "42" { t.Error("MapSnd should leave the first value \"42\" alone. Received:", Fst(res)) } @@ -69,3 +69,75 @@ func TestMapBoth(t *testing.T) { t.Error("MapBoth should return 42 as the second value. Received:", Snd(res)) } } + +func TestCheckFst_True(t *testing.T) { + res := CheckFst[string](func(x int) bool { return x > 10 })(Pair(42, "42")) + if res != true { + t.Error("CheckFst should return true. Received:", res) + } +} + +func TestCheckFst_False(t *testing.T) { + res := CheckFst[string](func(x int) bool { return x < 10 })(Pair(42, "42")) + if res != false { + t.Error("CheckFst should return false. Received:", res) + } +} + +func TestCheckSnd_True(t *testing.T) { + res := CheckSnd[int](func(x string) bool { return x == "42" })(Pair(42, "42")) + if res != true { + t.Error("CheckFst should return true. Received:", res) + } +} + +func TestCheckSnd_False(t *testing.T) { + res := CheckSnd[int](func(x string) bool { return x == "1" })(Pair(42, "42")) + if res != false { + t.Error("CheckFst should return false. Received:", res) + } +} + +func TestCheckBoth_True_True(t *testing.T) { + res := CheckBoth( + func(x int) bool { return x > 10 }, + func(x string) bool { return x == "42" }, + )(Pair(42, "42")) + + if res != true { + t.Error("CheckBoth should return true. Received:", res) + } +} + +func TestCheckBoth_True_False(t *testing.T) { + res := CheckBoth( + func(x int) bool { return x > 10 }, + func(x string) bool { return x != "42" }, + )(Pair(42, "42")) + + if res != false { + t.Error("CheckBoth should return false. Received:", res) + } +} + +func TestCheckBoth_False_True(t *testing.T) { + res := CheckBoth( + func(x int) bool { return x < 10 }, + func(x string) bool { return x == "42" }, + )(Pair(42, "42")) + + if res != false { + t.Error("CheckBoth should return false. Received:", res) + } +} + +func TestCheckBoth_False_False(t *testing.T) { + res := CheckBoth( + func(x int) bool { return x < 10 }, + func(x string) bool { return x != "42" }, + )(Pair(42, "42")) + + if res != false { + t.Error("CheckBoth should return false. Received:", res) + } +} From b5b260c5c2ce6431010fc53b78c94ee79af0dc7b Mon Sep 17 00:00:00 2001 From: Force4760 Date: Tue, 9 Aug 2022 11:30:05 +0100 Subject: [PATCH 4/7] Implement mergers --- pair/pair.go | 14 ++++++++++++++ pair/pair_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/pair/pair.go b/pair/pair.go index 971c985..7885680 100644 --- a/pair/pair.go +++ b/pair/pair.go @@ -67,3 +67,17 @@ func CheckBoth[A, B any](fnF func(A) bool, fnS func(B) bool) func(Tuple[A, B]) b return fnF(t.a) && fnS(t.b) } } + +// Merge the elements of a Tuple with a Curried function +func MergeC[A, B, C any](fn func(A) func(B) C) func(Tuple[A, B]) C { + return func(t Tuple[A, B]) C { + return fn(t.a)(t.b) + } +} + +// Merge the elements of a Tuple with a non-Curried function +func Merge[A, B, C any](fn func(A, B) C) func(Tuple[A, B]) C { + return func(t Tuple[A, B]) C { + return fn(t.a, t.b) + } +} diff --git a/pair/pair_test.go b/pair/pair_test.go index 5083516..fbd5537 100644 --- a/pair/pair_test.go +++ b/pair/pair_test.go @@ -141,3 +141,29 @@ func TestCheckBoth_False_False(t *testing.T) { t.Error("CheckBoth should return false. Received:", res) } } + +func TestMergeC(t *testing.T) { + mul := func(a int) func(int) int { + return func(b int) int { + return a * b + } + } + + res := MergeC(mul)(Pair(21, 2)) + + if res != 42 { + t.Error("MergeC should return 42. Received:", res) + } +} + +func TestMerge(t *testing.T) { + mul := func(a, b int) int { + return a * b + } + + res := Merge(mul)(Pair(21, 2)) + + if res != 42 { + t.Error("Merge should return 42. Received:", res) + } +} From e8552fc95bccb52b5594c0b1bd0f354f6be5fc3f Mon Sep 17 00:00:00 2001 From: Force4760 Date: Tue, 9 Aug 2022 11:54:47 +0100 Subject: [PATCH 5/7] Documentation and Tuple -> Pair --- README.md | 7 +++++ pair/pair.go | 76 +++++++++++++++++++++++------------------------ pair/pair_test.go | 40 ++++++++++++------------- 3 files changed, 65 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 6470cf7..117bf5a 100644 --- a/README.md +++ b/README.md @@ -243,3 +243,10 @@ Option represents encapsulation of an optional value, it might be used as the re You could instanciate an `opt.Option[T]` with a value with `opt.Some(val)`. If the value is missing you can use `opt.None[T]()`. Option exports `Some`, `None`, `IsSome`, `IsNone`, `GetOrElse`, `Match`, `Map`, `Chain`. + + +#### Pair + +Pair allows you to group 2 values into a single struct. Can be used to make a function with multiple returns return a sigle wrapped value + +Pair exports `New`, `Fst`, `Snd`, `Get`, `MapFst`, `MapSnd`, `MapBoth`, `CheckFst`, `CheckSnd`, `CheckBoth`, `Merge`, `MergeC` diff --git a/pair/pair.go b/pair/pair.go index 7885680..43c1dbe 100644 --- a/pair/pair.go +++ b/pair/pair.go @@ -1,83 +1,83 @@ package pair -// Base Tuple struct -type Tuple[A, B any] struct { +// Base Pair struct +type Pair[A, B any] struct { a A b B } -// Constructor for a Tuple -func Pair[A, B any](a A, b B) Tuple[A, B] { - return Tuple[A, B]{a, b} +// Constructor for a Pair +func New[A, B any](a A, b B) Pair[A, B] { + return Pair[A, B]{a, b} } -// Getter for the first value of the Tuple -func Fst[A, B any](t Tuple[A, B]) A { +// Getter for the first value of the Pair +func Fst[A, B any](t Pair[A, B]) A { return t.a } -// Getter for the second value of the Tuple -func Snd[A, B any](t Tuple[A, B]) B { +// Getter for the second value of the Pair +func Snd[A, B any](t Pair[A, B]) B { return t.b } -// Getter for both values of the Tuple -func Get[A, B any](t Tuple[A, B]) (A, B) { +// Getter for both values of the Pair +func Get[A, B any](t Pair[A, B]) (A, B) { return t.a, t.b } -// Execute the function on the first value of the Tuple -func MapFst[B, A, R any](fn func(A) R) func(Tuple[A, B]) Tuple[R, B] { - return func(t Tuple[A, B]) Tuple[R, B] { - return Tuple[R, B]{fn(t.a), t.b} +// Execute the function on the first value of the Pair +func MapFst[B, A, R any](fn func(A) R) func(Pair[A, B]) Pair[R, B] { + return func(t Pair[A, B]) Pair[R, B] { + return Pair[R, B]{fn(t.a), t.b} } } -// Execute the function on the second value of the Tuple -func MapSnd[A, B, R any](fn func(B) R) func(Tuple[A, B]) Tuple[A, R] { - return func(t Tuple[A, B]) Tuple[A, R] { - return Tuple[A, R]{t.a, fn(t.b)} +// Execute the function on the second value of the Pair +func MapSnd[A, B, R any](fn func(B) R) func(Pair[A, B]) Pair[A, R] { + return func(t Pair[A, B]) Pair[A, R] { + return Pair[A, R]{t.a, fn(t.b)} } } -// Execute the functions on both the first and second values of the Tuple -func MapBoth[A, B, R, S any](fnF func(A) R, fnS func(B) S) func(Tuple[A, B]) Tuple[R, S] { - return func(t Tuple[A, B]) Tuple[R, S] { - return Tuple[R, S]{fnF(t.a), fnS(t.b)} +// Execute the functions on both the first and second values of the Pair +func MapBoth[A, B, R, S any](fnF func(A) R, fnS func(B) S) func(Pair[A, B]) Pair[R, S] { + return func(t Pair[A, B]) Pair[R, S] { + return Pair[R, S]{fnF(t.a), fnS(t.b)} } } -// Helper to chcek if the first value of the Tuple satisfies a predicate -func CheckFst[B, A any](fn func(A) bool) func(Tuple[A, B]) bool { - return func(t Tuple[A, B]) bool { +// Helper to chcek if the first value of the Pair satisfies a predicate +func CheckFst[B, A any](fn func(A) bool) func(Pair[A, B]) bool { + return func(t Pair[A, B]) bool { return fn(t.a) } } -// Helper to chcek if the second value of the Tuple satisfies a predicate -func CheckSnd[A, B any](fn func(B) bool) func(Tuple[A, B]) bool { - return func(t Tuple[A, B]) bool { +// Helper to chcek if the second value of the Pair satisfies a predicate +func CheckSnd[A, B any](fn func(B) bool) func(Pair[A, B]) bool { + return func(t Pair[A, B]) bool { return fn(t.b) } } -// Helper to chcek if both the first and the second values of the Tuple satisfies their respective predicate -func CheckBoth[A, B any](fnF func(A) bool, fnS func(B) bool) func(Tuple[A, B]) bool { - return func(t Tuple[A, B]) bool { +// Helper to chcek if both the first and the second values of the Pair satisfies their respective predicate +func CheckBoth[A, B any](fnF func(A) bool, fnS func(B) bool) func(Pair[A, B]) bool { + return func(t Pair[A, B]) bool { return fnF(t.a) && fnS(t.b) } } -// Merge the elements of a Tuple with a Curried function -func MergeC[A, B, C any](fn func(A) func(B) C) func(Tuple[A, B]) C { - return func(t Tuple[A, B]) C { +// Merge the elements of a Pair with a Curried function +func MergeC[A, B, C any](fn func(A) func(B) C) func(Pair[A, B]) C { + return func(t Pair[A, B]) C { return fn(t.a)(t.b) } } -// Merge the elements of a Tuple with a non-Curried function -func Merge[A, B, C any](fn func(A, B) C) func(Tuple[A, B]) C { - return func(t Tuple[A, B]) C { +// Merge the elements of a Pair with a non-Curried function +func Merge[A, B, C any](fn func(A, B) C) func(Pair[A, B]) C { + return func(t Pair[A, B]) C { return fn(t.a, t.b) } } diff --git a/pair/pair_test.go b/pair/pair_test.go index fbd5537..e2dc786 100644 --- a/pair/pair_test.go +++ b/pair/pair_test.go @@ -2,32 +2,32 @@ package pair import "testing" -func TestPair(t *testing.T) { - res := Pair(42, "42") +func TestNew(t *testing.T) { + res := New(42, "42") if res.a != 42 { - t.Error("Pair should return a struct with 42 as the first value. Received:", res.a) + t.Error("New should return a struct with 42 as the first value. Received:", res.a) } if res.b != "42" { - t.Error("Pair should return a struct with \"42\" as the second value. Received:", res.b) + t.Error("New should return a struct with \"42\" as the second value. Received:", res.b) } } func TestFst(t *testing.T) { - res := Fst(Pair(42, "42")) + res := Fst(New(42, "42")) if res != 42 { t.Error("Fst should return 42. Received:", res) } } func TestSnd(t *testing.T) { - res := Snd(Pair(42, "42")) + res := Snd(New(42, "42")) if res != "42" { t.Error("Fst should return \"42\". Received:", res) } } func TestGet(t *testing.T) { - res1, res2 := Get(Pair(42, "42")) + res1, res2 := Get(New(42, "42")) if res1 != 42 { t.Error("Get should return 42 as the first value. Received:", res1) } @@ -37,7 +37,7 @@ func TestGet(t *testing.T) { } func TestMapFst(t *testing.T) { - res := MapFst[string](func(x int) int { return x + 1 })(Pair(41, "42")) + res := MapFst[string](func(x int) int { return x + 1 })(New(41, "42")) if Fst(res) != 42 { t.Error("MapFirst should return 42 as a first value. Received:", Fst(res)) } @@ -47,7 +47,7 @@ func TestMapFst(t *testing.T) { } func TestMapSnd(t *testing.T) { - res := MapSnd[string](func(x int) int { return x + 1 })(Pair("42", 41)) + res := MapSnd[string](func(x int) int { return x + 1 })(New("42", 41)) if Fst(res) != "42" { t.Error("MapSnd should leave the first value \"42\" alone. Received:", Fst(res)) } @@ -60,7 +60,7 @@ func TestMapBoth(t *testing.T) { res := MapBoth( func(x bool) bool { return !x }, func(x int) int { return x + 1 }, - )(Pair(false, 41)) + )(New(false, 41)) if Fst(res) != true { t.Error("MapBoth should return true as the first value. Received:", Fst(res)) @@ -71,28 +71,28 @@ func TestMapBoth(t *testing.T) { } func TestCheckFst_True(t *testing.T) { - res := CheckFst[string](func(x int) bool { return x > 10 })(Pair(42, "42")) + res := CheckFst[string](func(x int) bool { return x > 10 })(New(42, "42")) if res != true { t.Error("CheckFst should return true. Received:", res) } } func TestCheckFst_False(t *testing.T) { - res := CheckFst[string](func(x int) bool { return x < 10 })(Pair(42, "42")) + res := CheckFst[string](func(x int) bool { return x < 10 })(New(42, "42")) if res != false { t.Error("CheckFst should return false. Received:", res) } } func TestCheckSnd_True(t *testing.T) { - res := CheckSnd[int](func(x string) bool { return x == "42" })(Pair(42, "42")) + res := CheckSnd[int](func(x string) bool { return x == "42" })(New(42, "42")) if res != true { t.Error("CheckFst should return true. Received:", res) } } func TestCheckSnd_False(t *testing.T) { - res := CheckSnd[int](func(x string) bool { return x == "1" })(Pair(42, "42")) + res := CheckSnd[int](func(x string) bool { return x == "1" })(New(42, "42")) if res != false { t.Error("CheckFst should return false. Received:", res) } @@ -102,7 +102,7 @@ func TestCheckBoth_True_True(t *testing.T) { res := CheckBoth( func(x int) bool { return x > 10 }, func(x string) bool { return x == "42" }, - )(Pair(42, "42")) + )(New(42, "42")) if res != true { t.Error("CheckBoth should return true. Received:", res) @@ -113,7 +113,7 @@ func TestCheckBoth_True_False(t *testing.T) { res := CheckBoth( func(x int) bool { return x > 10 }, func(x string) bool { return x != "42" }, - )(Pair(42, "42")) + )(New(42, "42")) if res != false { t.Error("CheckBoth should return false. Received:", res) @@ -124,7 +124,7 @@ func TestCheckBoth_False_True(t *testing.T) { res := CheckBoth( func(x int) bool { return x < 10 }, func(x string) bool { return x == "42" }, - )(Pair(42, "42")) + )(New(42, "42")) if res != false { t.Error("CheckBoth should return false. Received:", res) @@ -135,7 +135,7 @@ func TestCheckBoth_False_False(t *testing.T) { res := CheckBoth( func(x int) bool { return x < 10 }, func(x string) bool { return x != "42" }, - )(Pair(42, "42")) + )(New(42, "42")) if res != false { t.Error("CheckBoth should return false. Received:", res) @@ -149,7 +149,7 @@ func TestMergeC(t *testing.T) { } } - res := MergeC(mul)(Pair(21, 2)) + res := MergeC(mul)(New(21, 2)) if res != 42 { t.Error("MergeC should return 42. Received:", res) @@ -161,7 +161,7 @@ func TestMerge(t *testing.T) { return a * b } - res := Merge(mul)(Pair(21, 2)) + res := Merge(mul)(New(21, 2)) if res != 42 { t.Error("Merge should return 42. Received:", res) From c70da844fb93c5c296add561adc917b6c79d727b Mon Sep 17 00:00:00 2001 From: Force4760 Date: Tue, 9 Aug 2022 13:08:16 +0100 Subject: [PATCH 6/7] Implement and Test Zip and EQ --- README.md | 2 +- pair/pair.go | 22 +++++++++++++ pair/pair_test.go | 78 +++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 91 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 117bf5a..66fe23d 100644 --- a/README.md +++ b/README.md @@ -249,4 +249,4 @@ Option exports `Some`, `None`, `IsSome`, `IsNone`, `GetOrElse`, `Match`, `Map`, Pair allows you to group 2 values into a single struct. Can be used to make a function with multiple returns return a sigle wrapped value -Pair exports `New`, `Fst`, `Snd`, `Get`, `MapFst`, `MapSnd`, `MapBoth`, `CheckFst`, `CheckSnd`, `CheckBoth`, `Merge`, `MergeC` +Pair exports `New`, `Fst`, `Snd`, `Get`, `MapFst`, `MapSnd`, `MapBoth`, `CheckFst`, `CheckSnd`, `CheckBoth`, `Merge`, `MergeC`, `Eq`, `Zip`. diff --git a/pair/pair.go b/pair/pair.go index 43c1dbe..4be59ee 100644 --- a/pair/pair.go +++ b/pair/pair.go @@ -81,3 +81,25 @@ func Merge[A, B, C any](fn func(A, B) C) func(Pair[A, B]) C { return fn(t.a, t.b) } } + +// Check two Pairs for element-by-element equality. The types must be comparable +func Eq[A, B comparable](p1 Pair[A, B]) func(Pair[A, B]) bool { + return func(p2 Pair[A, B]) bool { + return p1.a == p2.a && p1.b == p2.b + } +} + +// Take 2 lists of A and B and merge them into a single list of Pair[A, B] +// If the lists don't have the same size, the final list will have the same size as the smaller one +func Zip[B, A any](lstA []A) func([]B) []Pair[A, B] { + return func(lstB []B) (res []Pair[A, B]) { + i := 0 + + for i < len(lstA) && i < len(lstB) { + res = append(res, New(lstA[i], lstB[i])) + i++ + } + + return + } +} diff --git a/pair/pair_test.go b/pair/pair_test.go index e2dc786..554ae9b 100644 --- a/pair/pair_test.go +++ b/pair/pair_test.go @@ -2,6 +2,20 @@ package pair import "testing" +func eq[A, B comparable](ps1, ps2 []Pair[A, B]) bool { + if len(ps1) != len(ps2) { + return false + } + + for i := 0; i < len(ps1); i++ { + if !Eq(ps1[i])(ps2[i]) { + return false + } + } + + return true +} + func TestNew(t *testing.T) { res := New(42, "42") if res.a != 42 { @@ -143,27 +157,71 @@ func TestCheckBoth_False_False(t *testing.T) { } func TestMergeC(t *testing.T) { - mul := func(a int) func(int) int { - return func(b int) int { - return a * b - } - } + mul := func(a int) func(int) int { return func(b int) int { return a * b } } res := MergeC(mul)(New(21, 2)) - if res != 42 { t.Error("MergeC should return 42. Received:", res) } } func TestMerge(t *testing.T) { - mul := func(a, b int) int { - return a * b - } + mul := func(a, b int) int { return a * b } res := Merge(mul)(New(21, 2)) - if res != 42 { t.Error("Merge should return 42. Received:", res) } } + +func TestZip_EqLen(t *testing.T) { + res := Zip[bool]([]int{1, 2, 3, 4, 5})([]bool{true, true, false, false, true}) + want := []Pair[int, bool]{New(1, true), New(2, true), New(3, false), New(4, false), New(5, true)} + if !eq(res, want) { + t.Error("Zip should have returned", want, ". Received:", res) + } +} + +func TestZip_Large_Fst(t *testing.T) { + res := Zip[bool]([]int{1, 2, 3, 4, 5})([]bool{true, true, false}) + want := []Pair[int, bool]{New(1, true), New(2, true), New(3, false)} + if !eq(res, want) { + t.Error("Zip should have returned", want, ". Received:", res) + } +} + +func TestZip_Large_Snd(t *testing.T) { + res := Zip[bool]([]int{1, 2, 3})([]bool{true, true, false, false, true}) + want := []Pair[int, bool]{New(1, true), New(2, true), New(3, false)} + if !eq(res, want) { + t.Error("Zip should have returned", want, ". Received:", res) + } +} + +func TestEq_True_True(t *testing.T) { + res := Eq(New(42, 21))(New(42, 21)) + if res != true { + t.Error("Eq should return true. Received:", res) + } +} + +func TestEq_True_False(t *testing.T) { + res := Eq(New(42, 21))(New(42, 100)) + if res != false { + t.Error("Eq should return false. Received:", res) + } +} + +func TestEq_False_True(t *testing.T) { + res := Eq(New(21, 42))(New(100, 42)) + if res != false { + t.Error("Eq should return false. Received:", res) + } +} + +func TestEq_False_False(t *testing.T) { + res := Eq(New(42, 21))(New(84, 42)) + if res != false { + t.Error("Eq should return false. Received:", res) + } +} From f548a630ce0fbe627b29bce3c4c404932f4dee11 Mon Sep 17 00:00:00 2001 From: Force4760 Date: Wed, 10 Aug 2022 00:27:44 +0100 Subject: [PATCH 7/7] Pair docs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 66fe23d..c24b507 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Fp-go is a collection of Functional Programming helpers powered by Golang [1.18] - [Curry](#curry) - [Structs](#structs) - [Option](#option) + - [Pair](#pair) ## Install